Day 29: Unit Testing APIs with xUnit & Moq
Welcome to Day 29. Automated testing is the bedrock of professional engineering. If you refactor your OrderService, how do you know you didn’t break anything? You test it.
In .NET, the primary testing framework is xUnit.
Step 1: The Testing Project
Tests should live in a separate project so you don’t ship your test code into production binaries.
# 1. Create the API Project
dotnet new webapi -n MyApi
# 2. Create the Test Project
dotnet new xunit -n MyApi.Tests
# 3. Tell the Test Project about the API Project!
cd MyApi.Tests
dotnet add reference ../MyApi/MyApi.csproj
Step 2: The Logic
Assume we have a simple service in our API Project.
public interface ICalculator { int Add(int a, int b); }
public class CalculatorService : ICalculator
{
public int Add(int a, int b) => a + b;
}
Step 3: The xUnit Test
Inside the MyApi.Tests project, write a standard class and method decorated with the [Fact] attribute. The test framework automatically finds and runs it.
public class CalculatorTests
{
[Fact] // This tells xUnit that this method is a test!
public void Add_TwoPositiveNumbers_ReturnsCorrectSum()
{
// 1. Arrange (Set up the environment)
var calculator = new CalculatorService();
int a = 5;
int b = 10;
// 2. Act (Perform the action)
int result = calculator.Add(a, b);
// 3. Assert (Verify the outcome using xUnit's Assert class)
Assert.Equal(15, result);
}
}
To run it, open your terminal in the tests folder and type dotnet test.
Moq (Mocking Dependencies)
What if you want to test a Controller or Endpoint that heavily depends on a database? You absolutely do not want your Unit Tests connecting to a real production database!
Instead, we use a mocking framework like Moq to create fake versions of our dependencies.
dotnet add package Moq
public class UserControllerTests
{
[Fact]
public void GetUser_WhenUserExists_ReturnsOk()
{
// Arrange: Create a fake IUserRepository
var mockRepo = new Mock<IUserRepository>();
// Program the fake repository to return 'Alice' when asked for Id '1'
var fakeUser = new User { Id = 1, Username = "Alice" };
mockRepo.Setup(repo => repo.GetUserById(1)).Returns(fakeUser);
// Inject the fake repository into the real Controller!
var controller = new UsersController(mockRepo.Object);
// Act
var result = controller.GetUser(1);
// Assert: Ensure it returned a 200 OK!
var okResult = Assert.IsType<OkObjectResult>(result);
var returnedUser = Assert.IsType<User>(okResult.Value);
Assert.Equal("Alice", returnedUser.Username);
}
}
Challenge for Day 29
Write a failing test for a method you haven’t built yet (Test Driven Development). Create an IsEven(int number) test that asserts 4 returns true. Then, build the method to make the test pass in dotnet test!
Tomorrow: The Final Day — Docker & Deployment!