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

> Atlas copies your repo's existing xUnit conventions instead of inventing its own, then runs the new C# spec with dotnet test, because a test that was never executed is not a test.

Atlas writes unit tests for untested C# code by reading the target class, enumerating its exported symbols with the lsp tool's documentSymbol operation so no public method is missed, grepping for an existing test file to copy the repo's framework, import style, and naming convention, writing the new spec with the write tool, and then actually running it with xUnit via dotnet test through the bash tool. The run is the point. A test that was never executed is not a test.

## Key takeaways

- lsp documentSymbol enumerates every public C# member, so no method is quietly left untested.
- Atlas greps your existing xUnit spec and copies its conventions instead of inventing a competing pattern.
- The write tool shows the full C# test file as a diff in the permission prompt before it lands on disk.
- Atlas runs xUnit via dotnet test, because a test that was never executed is not a test.
- A todowrite entry per public member keeps a half-covered C# class from looking finished.

## How does Atlas add xUnit tests to an untested C# class?

Atlas reads the untested C# class, then calls the lsp tool's documentSymbol operation on it to enumerate every exported symbol, so a public method buried at the bottom of a 600 line OrderService.cs is not quietly skipped. Coverage of the public surface is the goal, not coverage of the obvious methods.

Reading a C# file top to bottom and testing what you remember produces exactly the gaps you would expect: the async overload gets tested, the sync one does not; the happy-path method gets tested, the internal validation helper promoted to public last quarter does not. documentSymbol returns the real member list from the C# language server, including every public method, property, and constructor. Atlas works that list, and where the class is large it keeps progress in a todowrite entry per member so a half-covered class does not look finished.

## How does Atlas match the repo's existing xUnit conventions instead of inventing its own?

Atlas greps for an existing test file in the C# solution and copies its framework, import style, and naming convention, step 2 of the documented workflow. If tests/OrderService.Tests/OrderServiceTests.cs already uses an xUnit Theory with InlineData and a shared fixture, the new spec looks the same rather than introducing a competing pattern.

Convention drift is the usual complaint about generated C# tests. An agent that does not look at your repo will happily write MSTest attributes into an xUnit project, invent its own AAA comment style, or new up dependencies that your solution injects through a fixture. Atlas greps first, through ripgrep with real regex plus include and path filters, scoped to your tests project. It reads the closest existing spec, copies the using directives, the fixture pattern, and the file naming, and writes the new test so it belongs in the solution.

## Why does Atlas run dotnet test instead of just writing the C# test file?

Atlas runs the new C# spec with xUnit via dotnet test through the bash tool, because a test that was never executed is not a test. Output over 2000 lines or 50 KB is truncated with the full log saved to a file, so a long MSBuild error trail is still fully readable.

The run is where the generated assertions meet reality. dotnet test compiles the test project against the real OrderService.cs, so a wrong parameter order, a missing NuGet reference, or a misunderstood nullable annotation surfaces immediately as a compiler error rather than as a green checkmark on a file nobody ran. When the output is long, the Atlas bash tool truncates at 2000 lines or 50 KB and saves the complete log to a file you can read, so a large xUnit run is still fully inspectable.

## How do you review C# test files before Atlas writes them to disk?

Atlas writes the new C# spec file with the write tool, which shows the diff in the permission prompt before anything lands on disk. Every Atlas tool call is permission-gated against 3 rule types, allow, ask, and deny, so tests/OrderService.Tests/OrderServiceTests.cs is created only after you approve the content.

You see the full file, as a diff, in the prompt. That is the review gate, and it applies to the dotnet test invocation too, so nothing runs in your solution outside your allow rules. Atlas also snapshots file changes as git patches, which means a test file that turns out to duplicate existing coverage can be diffed and rolled back cleanly. On a larger job, Atlas drafts a plan in a read-only plan agent and asks before switching to a build agent, so you can approve the member list before any C# is written.

## What happens when the generated C# tests fail on the first dotnet test run?

Atlas iterates with the edit tool until the xUnit suite is green, keeping progress in a todowrite list when the C# class is large. A first dotnet test run that reports 4 failures out of 12 is the expected starting point, not a failure of the workflow.

The first failures are usually informative rather than embarrassing. A test asserting the wrong exception type tells you the class throws something more specific. A test failing on a null argument tells you the guard clause behaves differently than the signature suggested. Atlas reads the failure, edits the spec or, where the production code is genuinely wrong, flags it, and re-runs dotnet test. dotnet format keeps the touched C# files consistent, and NuGet resolves any test package the existing convention required.

## Does Atlas understand a C# solution before it starts writing tests?

Atlas runs in a solution with a .csproj or .sln and reads your namespaces, project references, and NuGet packages first, the 3 documented C# setup steps. Atlas indexes C# code by AST declarations using tree-sitter, not blind line windows, so retrieval returns whole methods and classes rather than fragments.

That indexing choice is why Atlas can propose a correct xUnit test on the first attempt: when it retrieves OrderService.Validate, it gets the whole method, including the guard clauses and the exception it throws, rather than a window sliced through the middle. Atlas also searches C# code with hybrid semantic and keyword retrieval fused by reciprocal rank fusion, so asking for the class that handles refund eligibility finds it even if the class is not named that. If the code must stay local, Atlas can build the index with local Ollama embeddings.

## Steps

1. Run atlas in a solution with a .csproj or .sln and let Atlas read your namespaces, project references, and NuGet packages.
2. Read the C# class under test, then use the lsp tool's documentSymbol operation to enumerate its exported symbols so no public method is missed.
3. Grep the tests project for an existing spec to copy the repo's framework, using directives, fixture pattern, and file naming convention.
4. Write the new spec file with the write tool, which shows the diff in the permission prompt before anything lands on disk.
5. Run the suite with xUnit via dotnet test through the bash tool; output over 2000 lines or 50 KB is truncated and the full log is saved to a file you can read.
6. Iterate with edit until the xUnit suite is green, reviewing the unified diff Atlas surfaces for each change.
7. Keep progress in a todowrite list, one entry per public member, when the C# class is large.
8. Run dotnet format over the new test files and dotnet build before committing the coverage.

## FAQ

### how to generate xUnit tests for an untested C# class

Run atlas in your solution. Atlas reads the class, enumerates its public members with the lsp tool's documentSymbol operation, greps your tests project for the existing convention, writes the spec with the write tool, and runs xUnit via dotnet test until it is green.

### will an AI agent write C# tests that match our existing conventions

Atlas greps for an existing test file first and copies the repo's framework, using directives, fixture pattern, and naming convention rather than inventing its own. The new spec is written to look like it belongs in the solution.

### does Atlas actually run dotnet test or just write the test file

Atlas runs the suite with xUnit via dotnet test through the bash tool. A test that was never executed is not a test: running it is what catches a wrong parameter order, a missing NuGet reference, or a misunderstood nullable annotation.

### how does Atlas make sure no public C# method is left untested

The lsp tool's documentSymbol operation enumerates the class's exported symbols from the C# language server, so the member list is authoritative rather than remembered. On a large class, Atlas tracks one todowrite entry per member.

### can I review generated C# test files before they are written

Yes. The write tool shows the diff in the permission prompt before anything lands on disk, and every Atlas tool call is permission-gated against allow, ask, and deny rules. Atlas also snapshots file changes as git patches so a file can be rolled back.

### what if the dotnet test output is too long to read

The Atlas bash tool truncates inline output at 2000 lines or 50 KB and writes the complete log to a retained file, telling you the path. Atlas reads that file, so a large xUnit run is triaged against the full output.

### does Atlas run dotnet format on the tests it writes

Atlas can run dotnet format over the new C# files through the bash tool, subject to your allow, ask, and deny permission rules. Every tool call is permission-gated before it runs.

---

Canonical HTML: https://runatlas.sh/resources/stacks/write-unit-tests-for-untested-code-in-csharp
Source of truth: aeo_pages row `/resources/stacks/write-unit-tests-for-untested-code-in-csharp` (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.
