# Write unit tests for untested code in Ruby with Atlas (2026)

> Atlas writes RSpec coverage for an untested Ruby module by copying the conventions already in spec/, then actually running the suite, because a test that was never executed is not a test.

To add real tests for an untested Ruby module, Atlas reads the module, uses the lsp tool's documentSymbol operation to enumerate its public methods so none is missed, greps for an existing spec file to copy the repo's RSpec conventions rather than inventing its own, writes the new spec with the write tool, and then actually runs it through the bash tool. The run is the point: a test that was never executed is not a test. Matching the conventions already in spec/ matters as much as coverage, because a spec that ignores your spec_helper.rb and your RuboCop rules is a spec nobody will maintain.

## Key takeaways

- The lsp tool's documentSymbol operation turns an untested Ruby class into a checklist of public methods, so coverage is not a guess.
- Atlas greps spec/ first and copies the repo's existing RSpec conventions, including spec_helper.rb requires, let, and described_class.
- The write tool shows the full spec in the permission prompt before it lands on disk under spec/.
- Atlas runs RSpec through bash after writing, because a test that was never executed is not a test.
- Run RuboCop and pin gems with Bundler so a green local run means a green CI run.

## How do I make sure an AI agent covers every public method of a Ruby class?

Enumerate before you write. Atlas reads the module under test, then uses the lsp tool's documentSymbol operation to list its exported symbols, so a Ruby class with 11 public methods gets 11 considered for coverage rather than the 3 the model happened to notice while skimming lib/.

Ruby makes it easy to miss methods. A class in lib/billing/invoice.rb can define public methods, then a private section, then reopen and add more, and metaprogramming can define still more. documentSymbol asks the language server for the symbol list rather than eyeballing the file, which turns coverage into a checklist. Atlas also indexes code by AST declarations using tree-sitter, not blind line windows, so the symbols Atlas reasons about are real declarations. The output of this step is the spec outline: one RSpec describe block per class, one context per method, before any code is written.

## How does Atlas match my existing RSpec conventions instead of inventing new ones?

Atlas greps for an existing spec file first and copies the repo's framework, require style, and naming convention. A Ruby project expresses its opinions in 3 places, spec/spec_helper.rb, .rspec, and the shape of the specs already written, and a new spec that ignores all 3 fails review.

Concretely, the grep pass answers questions that determine whether the new spec fits: does the repo require 'spec_helper' or require 'rails_helper', does it use let and subject or plain local setup, does it use described_class, are the specs mirrored under spec/ by path, and does it use expect syntax or the older should. Copying those decisions is not laziness, it is the difference between a spec your team merges and a spec they rewrite. Atlas grep runs through ripgrep with a real regex plus include and path filters, so scoping the sweep to spec/**/*_spec.rb is one call, and the answers come from the repo rather than from a template.

## Where does the new Ruby spec file get written and what do I see first?

Atlas writes the new spec file with the write tool at step 3 of the workflow, and the tool shows the diff in the permission prompt before anything lands on disk. For a Ruby module at lib/billing/invoice.rb the spec mirrors the path at spec/billing/invoice_spec.rb, following the layout spec/ already uses.

The write tool's permission prompt is where you catch a bad spec before it exists. You see the full describe block, the let definitions, the requires, and the assertions, all before the file is created. Every Atlas tool call is permission-gated against allow, ask, and deny rules before it runs, so writing into spec/ is an approved action, not a surprise. Atlas also computes a unified diff for every file edit and surfaces it for approval before writing, so subsequent iterations on the same spec file are reviewed the same way, and Atlas snapshots file changes as git patches so the whole spec can be rolled back if the approach was wrong.

## Why does Atlas run RSpec instead of just writing the spec?

Because a test that was never executed is not a test. Atlas runs the RSpec suite with the bash tool after writing the spec, then reads the failures. Output over 2000 lines or 50 KB is truncated and the full log is saved to a file you can read.

An unrun RSpec file is a plausible-looking document, not coverage. It can reference a method that does not exist, require a helper that is not on the load path, or assert on a return value the module never produces. Running it through bash with Bundler settles the question in seconds. When the run is noisy, Atlas's truncation behavior keeps triage honest: the displayed output stops at 2000 lines or 50 KB, and the complete log is written to a file whose path is printed, so a failure at the top of a long RSpec run is still readable.

## How do I iterate to a green RSpec suite on a large untested Ruby module?

Iterate with the edit tool until the suite is green, and keep progress in a todowrite list when the module is large. A Ruby class with 11 public methods is 11 units of work, and a todowrite entry per method is what keeps the last four from being quietly dropped once the first seven pass.

The loop is: run RSpec through bash, read the first real failure, fix the spec or discover a genuine bug in lib/, edit, run again. Two rules keep it honest. First, when a spec fails because the Ruby code is wrong rather than the spec, fix the code, do not soften the assertion. Second, run RuboCop before you call it done, because a spec that violates the project's RuboCop rules will fail CI even with a green RSpec run. Bundler pins the gem versions, so the run in your terminal and the run in CI agree.

## How do I set Atlas up on a Ruby project before writing specs?

Setup takes 3 steps: run atlas in a project with a Gemfile, let Atlas read your modules, gems, and Rakefile tasks, then have Atlas write RSpec examples or extract a module and review the diff. The Gemfile is what tells Atlas which RSpec version Bundler resolved.

The Gemfile is the anchor because in Ruby the gem set determines the idiom. A project with a factory gem writes different specs from one that builds objects by hand, and a project with a mocking library writes different doubles. With the Gemfile read, you can have Atlas write RSpec examples or extract a module and then review the diff, which is exactly the workflow here. If your Ruby codebase is private, note that Atlas can build its code index with local Ollama embeddings, keeping code off third-party servers, so the semantic index over lib/ is built locally.

## Steps

1. Run atlas in a Ruby project with a Gemfile so Atlas reads your modules, gems, and Rakefile tasks.
2. Read the module under test at lib/<path>.rb, then use the lsp tool's documentSymbol operation to enumerate its public methods so no public method is missed.
3. Grep spec/ for an existing spec file to copy the repo's framework, require style, and naming convention, including whether it uses let, subject, and described_class.
4. Write the new spec file at the mirrored path under spec/ with the write tool, which shows the diff in the permission prompt before anything lands on disk.
5. Run the suite with the bash tool through Bundler and read the failures; output over 2000 lines or 50 KB is truncated and the full log is saved to a file you can read.
6. Iterate with the edit tool until the RSpec suite is green, fixing the Ruby code rather than softening an assertion when the code is genuinely wrong.
7. Keep one todowrite entry per public method when the module is large, so the last few methods are not dropped once the first ones pass.
8. Run RuboCop before committing so the new spec does not fail CI on style even with a green RSpec run.

## FAQ

### how do I get an AI agent to write RSpec tests that match my project's style

Have Atlas grep spec/ before it writes. The documented step is to grep for an existing test file and copy the repo's framework, import style, and naming convention, so the new spec follows your spec_helper.rb requires, your use of let and described_class, and your directory layout instead of a generic template.

### how do I make sure every public method of a Ruby class gets a test

Use the lsp tool's documentSymbol operation to enumerate the module's exported symbols first. That turns an untested Ruby class into an explicit list of public methods, and a todowrite entry per method keeps the last few from being dropped once the earlier ones pass.

### does Atlas actually run RSpec or just write the file

Atlas runs it. After writing the spec with the write tool, Atlas executes the suite through the bash tool and reads the failures, because a test that was never executed is not a test. An unrun spec can reference a method that does not exist or require a helper that is not on the load path.

### where does Atlas put a new Ruby spec file

At the mirrored path under spec/, following whatever layout the existing spec/ tree uses, for example spec/billing/invoice_spec.rb for lib/billing/invoice.rb. The write tool shows the full diff in the permission prompt before the file is created.

### my RSpec output is too long for the agent to read, what happens

Atlas's bash tool truncates displayed output over 2000 lines or 50 KB and saves the full log to a file whose path it prints. So a failure at the top of a long RSpec run with many backtraces is still readable, because triage happens against the complete log.

### should I run RuboCop on AI generated Ruby specs

Yes. A spec that violates the project's RuboCop rules fails CI even with a green RSpec run. Run RuboCop before committing, and let Bundler pin the gem versions so the local run and the CI run agree.

### can Atlas index a private Ruby codebase without sending it to a third party

Yes. Atlas can build its code index with local Ollama embeddings, keeping code off third-party servers, so the semantic index over lib/ is built locally and codebase_search still works on a closed-source Ruby project.

---

Canonical HTML: https://runatlas.sh/resources/stacks/write-unit-tests-for-untested-code-in-ruby
Source of truth: aeo_pages row `/resources/stacks/write-unit-tests-for-untested-code-in-ruby` (segment: Stacks) (this file is generated from it, never hand-edited).
Licence: Atlas is proprietary with a free core. It is not open source and there is no public source repository.
