Monday, December 8, 2025

thumbnail

Test-Driven Development (TDD) in .NET Core

 Test-Driven Development (TDD) in .NET Core


Test-Driven Development (TDD) is a software development process where tests are written before the actual code. TDD is popular because it helps improve code quality, provides immediate feedback to developers, and encourages better design. It consists of a short, repetitive cycle: Red, Green, and Refactor.


Here's how TDD works in practice, especially in a .NET Core context:


1️⃣ The TDD Cycle


The TDD cycle involves three basic steps, repeated iteratively:


1. Red: Write a Failing Test


Objective: Start by writing a test for a new feature or bug fix.


Result: The test should fail because the feature isn’t implemented yet.


Example: If you're adding a new method to your Calculator class, you write a test to check that method’s behavior.


public class CalculatorTests

{

    [Fact]

    public void Add_TwoNumbers_ReturnsCorrectSum()

    {

        // Arrange

        var calculator = new Calculator();


        // Act

        var result = calculator.Add(2, 3);


        // Assert

        Assert.Equal(5, result);

    }

}


2. Green: Make the Test Pass


Objective: Write the minimal code necessary to make the test pass.


Result: Once you write just enough code, the test should pass.


Example: Implement the Add method in the Calculator class so that the test passes.


public class Calculator

{

    public int Add(int a, int b)

    {

        return a + b;

    }

}



Now, the test will pass because the Add method works as expected.


3. Refactor: Improve the Code


Objective: Refactor the code without changing its behavior.


Result: Clean up any redundancies, improve the design, or optimize the code.


Example: In this simple case, the code is already minimal, but in a more complex example, you might refactor for readability, performance, or maintainability.


2️⃣ Setting Up TDD in .NET Core

1. Create a New .NET Core Project


You can set up TDD in a new or existing .NET Core project by creating a Unit Test Project.


Create an ASP.NET Core Web API:


dotnet new webapi -n TDDExample



Add a Test Project:


dotnet new xunit -n TDDExample.Tests



Add the Test Project to Solution:


dotnet sln add TDDExample.Tests/TDDExample.Tests.csproj



Add a Reference to the API Project:


dotnet add TDDExample.Tests reference TDDExample


2. Install Required NuGet Packages


xUnit for writing tests.


Moq or NSubstitute for mocking dependencies (if needed).


FluentAssertions for more readable assertions (optional).


dotnet add package xunit

dotnet add package Moq

dotnet add package FluentAssertions


3. Write Your First Test


Once the test project is set up, you can begin following the TDD cycle.


For example, let’s say you’re building a UserService to fetch user data from a database.


Step 1: Write a Failing Test

public class UserServiceTests

{

    [Fact]

    public void GetUserById_ReturnsUser_WhenUserExists()

    {

        // Arrange

        var mockRepo = new Mock<IUserRepository>();

        mockRepo.Setup(repo => repo.GetUserById(1)).Returns(new User { Id = 1, Name = "John Doe" });


        var userService = new UserService(mockRepo.Object);


        // Act

        var result = userService.GetUserById(1);


        // Assert

        Assert.Equal(1, result.Id);

        Assert.Equal("John Doe", result.Name);

    }

}


Step 2: Implement the Code to Make the Test Pass

public class UserService

{

    private readonly IUserRepository _userRepository;


    public UserService(IUserRepository userRepository)

    {

        _userRepository = userRepository;

    }


    public User GetUserById(int id)

    {

        return _userRepository.GetUserById(id);

    }

}


Step 3: Refactor the Code


After the test passes, you might notice that you can refactor the method or even improve your mock setup, but the core logic should remain unchanged.


For example:


public User GetUserById(int id)

{

    var user = _userRepository.GetUserById(id);

    return user ?? throw new Exception("User not found");

}


3️⃣ Best Practices for TDD in .NET Core


Write Simple Tests First: Start by writing simple, straightforward tests that directly relate to the behavior you're implementing.


Test One Thing at a Time: Each test should focus on one behavior, whether it’s a simple calculation or a feature of your service.


Small, Frequent Commits: Make frequent, small commits to maintain a steady pace. This allows you to isolate errors and maintain high code quality.


Refactor with Caution: After tests pass, refactor code, but be mindful of maintaining test coverage for new changes.


Test Coverage & Edge Cases: Aim to cover edge cases, such as handling nulls, exceptions, or special data. Don’t just test the “happy path.”


Avoid Over-Mocking: Mock external services or repositories but try to avoid mocking business logic or simple methods. This keeps your tests meaningful.


Use Dependency Injection: Leverage dependency injection to make it easy to replace real dependencies with mocks during testing. .NET Core's built-in DI system makes this easy.


4️⃣ Example of TDD for a Full Use Case


Let’s walk through TDD for a small application where we build a ProductService that interacts with a ProductRepository. The ProductService will have a method that returns a list of products by category.


Step 1: Write a Failing Test

public class ProductServiceTests

{

    [Fact]

    public void GetProductsByCategory_ReturnsCorrectProducts()

    {

        // Arrange

        var mockRepo = new Mock<IProductRepository>();

        var products = new List<Product>

        {

            new Product { Id = 1, Name = "Product A", Category = "Electronics" },

            new Product { Id = 2, Name = "Product B", Category = "Electronics" }

        };

        mockRepo.Setup(repo => repo.GetProductsByCategory("Electronics")).Returns(products);


        var productService = new ProductService(mockRepo.Object);


        // Act

        var result = productService.GetProductsByCategory("Electronics");


        // Assert

        Assert.Equal(2, result.Count());

        Assert.Contains(result, p => p.Name == "Product A");

    }

}


Step 2: Make the Test Pass

public class ProductService

{

    private readonly IProductRepository _productRepository;


    public ProductService(IProductRepository productRepository)

    {

        _productRepository = productRepository;

    }


    public IEnumerable<Product> GetProductsByCategory(string category)

    {

        return _productRepository.GetProductsByCategory(category);

    }

}


Step 3: Refactor


Refactor the code to optimize the logic or extract the method into smaller parts.


public IEnumerable<Product> GetProductsByCategory(string category)

{

    var products = _productRepository.GetProductsByCategory(category);

    return products.Where(p => p.Category == category);

}


5️⃣ Benefits of TDD in .NET Core


Improved Code Quality: TDD encourages you to think about the code’s design and behavior from the very beginning.


Immediate Feedback: You get instant feedback after each code change, allowing quick identification of bugs.


Maintainability: Since tests ensure that your code behaves as expected, refactoring becomes safer and easier.


Comprehensive Documentation: Tests serve as documentation for how the code is supposed to work.


Confidence in Changes: You can confidently change or extend your code, knowing that tests will catch regressions.


6️⃣ Conclusion


Test-Driven Development in .NET Core is a powerful technique for writing clean, maintainable, and bug-free code. The Red-Green-Refactor cycle helps developers focus on writing tests before code and ensures that each part of the application is thoroughly tested.


Red: Write the failing test.


Green: Make it pass.


Refactor: Improve the code without changing functionality.


By following TDD, you ensure that your application behaves as expected and is easier to maintain and extend over time.

Learn Dot Net Course in Hyderabad

Read More

Mocking and Stubbing in Unit Testing for .NET Core

Writing Automated UI Tests for Full Stack .NET Applications

Testing Web APIs in .NET with Postman and xUnit

Testing Web APIs in .NET with Postman and xUnit

Visit Our Quality Thought Institute in Hyderabad

Get Directions 

Subscribe by Email

Follow Updates Articles from This Blog via Email

No Comments

About

Search This Blog

Powered by Blogger.

Blog Archive