Stacks

Write Unit Tests for Untested Code in Scala with Atlas (2026)

Updated 9 min read

To add unit tests to an untested Scala module, Atlas reads the module first, then copies the repo's existing test conventions rather than inventing its own. Atlas uses the lsp tool's documentSymbol operation to enumerate every exported symbol in the file under src/main/scala, so no public method or object is quietly skipped, and greps an existing spec under src/test/scala to learn which ScalaTest style the codebase actually uses, AnyFunSuite or AnyFlatSpec, and how its matchers are imported. Atlas writes the new spec with the write tool, which shows the diff in the permission prompt before anything lands on disk, and then actually runs it with ScalaTest via sbt test through the bash tool. The run is the point: a test that was never executed is not a test.

How does Atlas write unit tests for an untested Scala module?

Atlas reads the untested Scala module, then uses the lsp tool's documentSymbol operation to enumerate its exported symbols, so none of the 3 kinds of public entry point in src/main/scala is missed: case class methods, companion-object apply, and trait defaults inherited by every class that mixes them in.

Scala hides its public surface in ways that reward a symbol-level enumeration. A companion object adds apply and unapply that are as public as any def. A trait with a default implementation contributes methods to every class that mixes it in. An implicit class extends a type that lives in another package entirely. documentSymbol returns the declaration list from the language server, and Atlas turns that list into the test plan, one case per public entry point. Atlas also indexes code by AST declarations using tree-sitter rather than blind line windows, so the module's defs and objects are retrievable as whole declarations rather than as arbitrary line spans.

How does Atlas match the ScalaTest conventions the repo already uses?

Atlas greps an existing spec under src/test/scala before writing anything, to copy the repo's framework, import style, and naming convention. Scala projects differ sharply on all 3: 1 repo uses ScalaTest's AnyFunSuite with test("..."), another uses AnyFlatSpec with should, and a spec in the wrong style is a review comment waiting to happen.

Convention matching is not cosmetic in a Scala codebase. The base class a spec extends determines which syntax works at all, and the matchers import determines whether shouldBe compiles. A repo that already has a shared BaseSpec trait wired into build.sbt expects the new spec to extend it, and a repo that registers test dependencies as a Test-scoped library in build.sbt expects the new spec to use only those. Atlas greps for an existing spec, reads it, and mirrors it. Atlas searches with hybrid semantic and keyword retrieval fused by reciprocal rank fusion, so even a loosely worded query for the repo's test base class tends to surface the right file.

How does Atlas write a new Scala spec file safely?

Atlas writes the new Scala spec with the write tool, which shows the diff in the permission prompt before anything lands on disk. A new file under src/test/scala/com/example/BillingServiceSpec.scala is reviewed in full before creation, and every Atlas tool call is gated against 3 rule kinds first: allow, ask, and deny.

New test files are the one category where an agent is tempted to write a lot at once, so the approval step earns its keep. The permission prompt shows the whole file, which is where you catch the test that asserts on an implementation detail instead of on behavior, the case that duplicates an existing spec, and the assertion that would pass no matter what the code does. Atlas snapshots file changes as git patches, so even an approved spec that turns out to be wrong can be diffed and rolled back rather than deleted by hand. The formatting pass with scalafmt comes at the end, once the spec is green.

Why must Atlas actually run the Scala test suite it wrote?

Atlas runs the new spec with ScalaTest via sbt test through the bash tool, which truncates output over 2000 lines or 50 KB and saves the full log, because a test that was never executed is not a test. A ScalaTest spec can compile under sbt and still assert nothing, and only running it proves otherwise.

The run also produces the feedback loop. sbt test output over 2000 lines or 50 KB is truncated inline, and Atlas's bash tool writes the complete log to a retained file and reports the path, so a large Scala compile plus a full ScalaTest run remains fully readable rather than tailed. Atlas reads the failures out of that log and iterates with edit until the suite is green. When the module is large enough that its public surface runs to dozens of symbols, Atlas keeps progress in a todowrite list, one entry per untested symbol, so a spec that covers half the object is not mistaken for done.

How do you keep Scala test coverage honest on a large module?

Atlas keeps a todowrite list, one entry per public symbol returned by the lsp tool's documentSymbol operation, when the Scala module is large. A spec file that grows to 300 lines and covers 6 of 14 public methods looks complete from the outside, and only an explicit checklist makes the gap visible.

The list is checked off against reality, not against intent: an entry is done when a ScalaTest case for that symbol exists and ScalaTest via sbt test passes with it. Working the list one symbol at a time also keeps each sbt run cheap, because sbt recompiles incrementally and a small spec addition does not trigger a full rebuild of the module. When the list is clear, run the full ScalaTest via sbt test once more, then run scalafmt against the new spec so it matches .scalafmt.conf and the diff in review is about tests rather than about line breaks.

How do you set up Atlas on a Scala project in 2026?

Run atlas in a Scala project with a build.sbt in 2026 and let Atlas read your traits, implicits, and sbt modules. Atlas is a terminal-native TUI, so it runs in the same shell where you already invoke sbt. Have Atlas refactor to typeclasses or add ScalaTest cases, then review the diff before it lands.

Reading build.sbt is what lets Atlas place a new spec in the right sbt module, which matters in a multi-project Scala build where each subproject has its own src/test/scala tree and its own Test-scoped dependencies. Permissions are worth configuring for this workflow: allowing sbt under bash while leaving write and edit on ask means Atlas can compile and run ScalaTest freely while every new spec file still gets an explicit approval. Teams that cannot send source to a hosted model can build the Atlas code index with local Ollama embeddings, keeping the Scala codebase off third-party servers.

Step by step

  1. 01Run atlas in the Scala project that contains build.sbt, and let it read your traits, implicits, and sbt modules.
  2. 02Read the untested module under src/main/scala, then use the lsp tool's documentSymbol operation to enumerate its exported symbols so no public def, object, or trait method is missed.
  3. 03Grep an existing spec under src/test/scala to copy the repo's framework, import style, and naming convention: AnyFunSuite versus AnyFlatSpec, the matchers import, any shared BaseSpec trait.
  4. 04Have Atlas write the new spec file with the write tool, which shows the diff in the permission prompt before anything lands on disk.
  5. 05Run the suite with ScalaTest via sbt test through the bash tool. Output over 2000 lines or 50 KB is truncated inline and the full log is saved to a file you can read.
  6. 06Read the failures from the saved log and iterate with edit until ScalaTest via sbt test is green.
  7. 07For a large module, keep one todowrite entry per public symbol so a spec that covers 6 of 14 methods is not mistaken for finished.
  8. 08Run scalafmt on the new spec so it matches .scalafmt.conf, then review the final diff before committing.

Frequently asked questions

how to add unit tests to an untested Scala module with an AI agent
Have Atlas read the module, enumerate its exported symbols with the lsp tool's documentSymbol operation, grep an existing spec under src/test/scala to copy the repo's ScalaTest conventions, write the new spec with the write tool, and run ScalaTest via sbt test through bash until it is green.
how do I make an AI agent match my repo's ScalaTest style
Atlas greps an existing spec before writing, so it copies the framework, import style, and naming convention already in use, whether that is AnyFunSuite with test("...") or AnyFlatSpec with should, plus any shared BaseSpec trait wired into build.sbt.
does Atlas actually run the Scala tests it writes
Yes. Atlas runs the new spec with ScalaTest via sbt test through its bash tool, because a test that was never executed is not a test. A ScalaTest spec can compile under sbt and still assert nothing, and only running it proves otherwise.
how do I make sure every public Scala method gets a test
Use the lsp tool's documentSymbol operation to enumerate the module's exported symbols, then keep one todowrite entry per symbol. Companion-object apply methods and trait defaults are public too, and a read of the file top to bottom tends to miss them.
what happens when sbt test output is too long for the agent to read
Atlas's bash tool truncates inline output at 2000 lines or 50 KB, writes the complete log to a retained file, and reports the path. Atlas reads the saved log, so a large Scala compile plus a full ScalaTest run is triaged against the whole output rather than a tail.
does Atlas support multi-project sbt builds
Yes. Run atlas in a project with a build.sbt and it reads your traits, implicits, and sbt modules, which is what lets it place a new spec in the correct subproject's src/test/scala tree with that subproject's Test-scoped dependencies.
when should scalafmt run on a new Scala spec
Run scalafmt at the end, after ScalaTest via sbt test is green, so the review diff is about the tests rather than about line breaks. Atlas surfaces the diff for every file edit before writing, so the formatting pass is reviewed too.

Try Atlas in your terminal

The terminal-native AI coding agent. Free core, single binary.

Install Atlas

Related guides

Write Unit Tests for Untested Code with Atlas in 2026

How to write unit tests for untested code with Atlas in 2026: the lsp tool enumerates exported symbols, grep copies repo conventions, and bash actually runs the suite.

Atlas for Scala in 2026

Atlas is a terminal-native AI coding agent for Scala in 2026. Run it in a project with a build.sbt, let it read your traits and implicits, and approve every diff.

Migrate a deprecated API across every callsite in Scala with Atlas in 2026

Effortlessly migrate deprecated Scala APIs across your entire codebase using Atlas. Discover every callsite, apply context-aware patches, and verify with `sbt test` for a complete, safe transition in 2026.

Rename a symbol across the repo in Scala with Atlas in 2026

Refactor Scala code with Atlas in 2026. Safely rename functions, classes, or constants across your entire sbt project, ensuring all references, including comments and strings, are updated accurately.

Locate where a behavior is implemented in Scala with Atlas (2026)

Find the Scala file and symbol behind a behavior in 2026 with Atlas: codebase_search for meaning, grep through ripgrep for exact text, and the lsp tool for symbols.

Run the Test Suite and Triage the Failures in Scala with Atlas (2026)

Atlas runs ScalaTest via sbt test with a generous timeout, retains the full log when output truncates at 2000 lines, and turns red output into a todowrite list.

Diagnose a hanging or long-running command in Scala with Atlas (2026)

Is your sbt build slow or blocked on stdin? Atlas races every command against a timeout and tells you which. A 2026 guide for Scala teams using sbt and scalafmt.

Trace a runtime bug from a stack trace in Scala with Atlas (2026)

How Atlas turns a Scala stack trace into the responsible line in 2026: read each frame, grep the error string, walk callers with lsp, fix, then sbt test.

Browse this resource hub