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.