Lesson 7 of 40 Testing Intermediate 45 min

Unit Testing with xUnit 3 and Copilot

In this lesson, you will learn how to write better unit tests with xUnit 3, generate test ideas with Copilot, use mocks to isolate dependencies, and measure how much of your code is covered by tests.

← Back to Visual Studio 2026 Tutorial Home

What you will learn

Why this matters: Unit tests help you catch regressions early, verify business rules, and refactor code with more confidence.

Part 1: Understanding xUnit 3

xUnit is a popular testing framework for .NET. It lets you write automated tests that verify whether your code behaves as expected. xUnit 3 continues the same clean style while improving the testing experience for modern .NET development.

A unit test usually focuses on one small piece of behavior, such as a calculation, validation rule, or service method.

// Theory with ClassData
[Theory]
[ClassData(typeof(OrderTestData))]
public async Task ProcessOrder_ValidatesTotal(Order order, decimal expected)
{
  var result = await _service.ProcessAsync(order);
  Assert.Equal(expected, result.Total);
}

In this example, the same test logic runs with different data inputs. That is useful when you want to verify the same rule under multiple conditions.

Part 2: Writing effective unit tests

A good unit test should be easy to read, easy to maintain, and focused on one behavior. A common pattern is:

Tip: Name test methods clearly so they describe what is being tested, such as CalculateTotal_ReturnsZero_WhenCartIsEmpty.

Part 3: AI-assisted test generation with Copilot

Visual Studio 2026 can use Copilot to help generate test ideas and boilerplate. This can save time, especially when writing repetitive test setups or identifying edge cases you may have missed.

For example, Copilot may suggest tests for:

Important: Copilot can speed up test writing, but you should still review every generated test. A generated test may compile yet still fail to validate the behavior you actually care about.

Part 4: Mocking dependencies with NSubstitute

Many methods depend on repositories, APIs, loggers, or other services. In unit tests, you usually do not want to call the real dependency. Instead, you replace it with a mock or substitute.

var repo = Substitute.For<IOrderRepository>();
repo.GetByIdAsync(Arg.Any<int>())
  .Returns(Task.FromResult(new Order { Id = 1, Total = 99.99m }));

// Verify it was called
await repo.Received(1).GetByIdAsync(1);

This lets you isolate the method under test and verify how it interacts with other components.

Part 5: Code coverage and Hot Coverage

Code coverage shows which parts of your code were executed by your tests. It does not guarantee correctness, but it helps reveal areas that have not been tested at all.

Visual Studio 2026 includes Hot Coverage, which can show live coverage information while you work.

Reminder: High coverage is not the same as high-quality testing. It is possible to have many tests and still miss important scenarios.

When to use each testing tool

Tool or concept Best used for
xUnit Writing and organizing automated unit tests
Theory and data-driven tests Testing the same logic with multiple input values
Copilot test generation Getting test suggestions and saving setup time
NSubstitute Replacing dependencies with test doubles
Coverage tools Finding code paths that are not exercised by tests

A practical unit testing workflow

Step 1: Identify a small behavior to test
Step 2: Arrange test data and substitute dependencies if needed
Step 3: Execute the method under test
Step 4: Assert the expected result clearly
Step 5: Add edge-case tests for unusual inputs
Step 6: Review coverage and refine weak tests

Best practices

Summary

In this lesson, you learned how xUnit 3 supports unit testing in modern .NET projects, how Copilot can help generate tests, how mocking frameworks isolate dependencies, and how coverage tools reveal untested areas.

In the next lesson, you will explore performance profiling and diagnostics in Visual Studio 2026.