Skip to content
Notifications
Clear all

Has anyone tried using Copilot for writing unit tests in a legacy C# codebase?

2 Posts
2 Users
0 Reactions
28 Views
(@backend_builder)
Prominent Member
Joined: 6 months ago
Posts: 605
Topic starter   [#10192]

I've been using Copilot extensively for Python/Go in greenfield projects, and it's a dream for generating boilerplate CRUD or API client tests. But my current gig has me deep in a legacy C# monolith (.NET Framework 4.7.2, NUnit, lots of untested business logic). I decided to see if Copilot could handle the gnarlier context of old C#.

My initial take: **it's surprisingly helpful for scaffolding, but you have to hold its hand through the complex dependencies.**

For example, given a simple legacy service method:

```csharp
public class OrderProcessor
{
private ILegacyInventoryService _inventory;
public OrderProcessor(ILegacyInventoryService inventory)
{
_inventory = inventory;
}
public ProcessResult ProcessOrder(Order order)
{
// Complex legacy logic with multiple branches
if (order == null) return ProcessResult.Invalid;
// ... more logic
}
}
```

Copilot quickly suggested a basic test structure when I typed `[Test]` and started writing:

```csharp
[Test]
public void ProcessOrder_OrderIsNull_ReturnsInvalid()
{
var inventoryMock = new Mock();
var processor = new OrderProcessor(inventoryMock.Object);
var result = processor.ProcessOrder(null);
Assert.AreEqual(ProcessResult.Invalid, result);
}
```

That's decent. But the real challenge is when the class has 5+ dependencies, some with no interfaces, or uses static calls. Copilot struggles to invent mocks for those without clear examples in the codebase. You often end up:

* Writing the Arrange section yourself first, then it helps with Act/Assert.
* Needing to create the mocks manually before it can use them correctly.
* Correcting its suggestions for mocking frameworks (it assumed Moq, which was right, but sometimes suggested NSubstitute syntax).

**Key observations:**
* It's fantastic for filling in repetitive test cases for different input conditions.
* It can guess edge cases you might miss (like empty strings, zero values) based on parameter names.
* It's less reliable for suggesting how to refactor untestable code patterns (e.g., static DateTime.Now calls).
* The context window seems to pull from nearby test files, which helps if your project already has some testing patterns.

Has anyone else thrown it at a messy, older C# codebase? Did you find it accelerated writing meaningful tests, or did the overhead of correcting its assumptions outweigh the benefits? I'm curious about your experiences with mocking heavy, tightly-coupled code.

--builder


Latency is the enemy, but consistency is the goal.


   
Quote
(@danm)
Honorable Member
Joined: 3 months ago
Posts: 452
 

Yep, exactly. I've found it gets lost quickly if there's a weird static method call or some sealed class you can't mock. Once you stub out those dependencies yourself, it usually picks up the thread again for the arrange/act/assert flow.

It saved me a ton of time just generating the repetitive test cases for all the null/empty guard clauses in our old services.



   
ReplyQuote