Post

C# | TDD Example using xUnit and Moq

In Test-Driven Development (TDD), we write tests before writing the actual code. This example demonstrates how to create unit tests in C# using xUnit, how to use the Moq framework for mocking, and how to refactor tests using Fact and Theory attributes.

Prerequisites

Before we start, make sure you have the following installed:

  • Visual Studio or Visual Studio Code (or any C# IDE of your choice)
  • xUnit.net testing framework
  • Moq mocking framework

Scenario

Suppose we are building a simple library that calculates the total price of items in a shopping cart.

Step 1: Set Up the Project

Create a new C# project and add references to the xUnit and Moq libraries.

Step 2: Write the Initial Test

Let’s start by writing a test for our shopping cart. Create a test class, and write a Fact that checks if the cart’s total price is calculated correctly:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using System;
using Xunit;

public class ShoppingCartTests
{
    [Fact]
    public void CalculateTotalPrice_CartWithItems_ReturnsTotalPrice()
    {
        // Arrange
        var cart = new ShoppingCart();
        cart.AddItem(new Item("Item 1", 10.0));
        cart.AddItem(new Item("Item 2", 15.0));

        // Act
        var totalPrice = cart.CalculateTotalPrice();

        // Assert
        Assert.Equal(25.0, totalPrice);
    }
}

Step 3: Write the Initial Code

Now, create the ShoppingCart class and implement the CalculateTotalPrice method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ShoppingCart
{
    private List<Item> items = new List<Item>();

    public void AddItem(Item item)
    {
        items.Add(item);
    }

    public double CalculateTotalPrice()
    {
        return items.Sum(item => item.Price);
    }
}

Step 4: Mocking with Moq

Suppose our ShoppingCart class depends on an external data source (e.g., a database). To test it, we can use Moq to mock this dependency. Create an interface for the data source, implement it, and inject it into the ShoppingCart:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public interface IDataSource
{
    List<Item> GetItems();
}

public class Database : IDataSource
{
    public List<Item> GetItems()
    {
        // Simulate database call
        return new List<Item>
        {
            new Item("Item 1", 10.0),
            new Item("Item 2", 15.0)
        };
    }
}

public class ShoppingCart
{
    private IDataSource dataSource;

    public ShoppingCart(IDataSource dataSource)
    {
        this.dataSource = dataSource;
    }

    // ...
}

Step 5: Refactor the Test with Theory

Instead of Fact, let’s refactor the test using Theory. This allows us to use data from multiple test cases. For instance, we can test the CalculateTotalPrice method with different sets of items:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Theory]
[InlineData(new[] { 10.0, 15.0 }, 25.0)]
[InlineData(new[] { 5.0, 7.0, 8.0 }, 20.0)]
public void CalculateTotalPrice_CartWithItems_ReturnsTotalPrice(double[] itemPrices, double expectedTotalPrice)
{
    // Arrange
    var cart = new ShoppingCart(CreateMockDataSource(itemPrices));

    // Act
    var totalPrice = cart.CalculateTotalPrice();

    // Assert
    Assert.Equal(expectedTotalPrice, totalPrice);
}

Here, we’re using Theory to run the test with different sets of item prices. The CreateMockDataSource function sets up a Moq mock of the IDataSource interface.

What Next?

This example demonstrates how to use TDD with xUnit, Moq for mocking, and how to refactor tests using Fact and Theory attributes. By following TDD, you can ensure that your code is thoroughly tested and that it meets the specified requirements.

This post is licensed under CC BY 4.0 by the author.