Writing Unit Tests in .NET Core C# using XUnit and Mocking Dependencies with FakeItEasy

Susitha Bandara
9 min readApr 15, 2023

--

Unit testing is a crucial part of software development. It helps to ensure that the code is correct, reliable and functions as expected. In .NET Core, there are various unit testing frameworks available, including NUnit, MSTest, and XUnit. XUnit is an open-source testing framework designed to write unit tests in .NET Core C#. In this article, we will discuss how to write unit tests using XUnit and mock dependencies using the FakeItEasy plugin.

Setting up the Project

Before we start writing unit tests, we need to create a .NET Core project. We can create a new project using the following command in the terminal:

dotnet new console -n Calculator

Suppose we are going to write unit tests for the following implementation of a Calculator:

public class CalculatorService : ICalculatorService
{
private readonly ICalculatorRepository _calculatorRepository;

public CalculatorService(ICalculatorRepository calculatorRepository)
{
_calculatorRepository = calculatorRepository;
}

public int Add(int x, int y)
{
_calculatorRepository.LogCalculation(x, y, "Add");
return x + y;
}

public int Subtract(int x, int y)
{
_calculatorRepository.LogCalculation(x, y, "Subtract");
return x - y;
}

public int Multiply(int x, int y)
{
_calculatorRepository.LogCalculation(x, y, "Multiply");
return x * y;
}

public int Divide(int x, int y)
{
_calculatorRepository.LogCalculation(x, y, "Divide");

if (y == 0)
{
throw new DivideByZeroException("Cannot divide by zero");
}

return x / y;
}
}

This implementation defines a CalculatorService class that implements the ICalculatorService interface. The constructor of this class takes an ICalculatorRepository object as an argument and stores it in a private field for later use. The four methods of the ICalculatorService interface are implemented here with each method calling the corresponding method of the ICalculatorRepository interface with the input values and operation name.

Once we have created the project, we need to add the required packages to our project. In this case, we need to add the XUnit and FakeItEasy packages. We can add these packages using the following commands:

dotnet add package xunit
dotnet add package FakeItEasy

Once we have added the packages, we need to create a class to write our unit tests. Let’s create a class called “CalculatorTests.cs” in the project:

using Xunit;
using FakeItEasy;

public class CalculatorTests
{
private readonly ICalculatorService _calculatorService;

public CalculatorTests()
{
_calculatorService = A.Fake<ICalculatorService>();
}
}

In the above code, we have added the XUnit and FakeItEasy namespaces and created a class called “CalculatorTests.” We have also created a field called “_calculatorService” of type “ICalculatorService.” We will use this field to mock our dependencies using the FakeItEasy plugin.

Writing Unit Tests

Now that we have set up our project and created a class for our unit tests, let’s start writing our first test. We will create a method called “Test_Addition” that will test the addition functionality of our calculator service. Here is how we can implement this test:

[Fact]
public void Test_Addition()
{
// Arrange
A.CallTo(() => _calculatorService.Add(A<int>.Ignored, A<int>.Ignored))
.Returns(5);

// Act
var result = _calculatorService.Add(2, 3);

// Assert
Assert.Equal(5, result);
}

In the above code, we have added a method called “Test_Addition” and decorated it with the “Fact” attribute. This attribute indicates that this method is a unit test. We have also arranged the test by using the FakeItEasy plugin to mock the “Add” method of the “ICalculatorService” interface. We have specified that when the “Add” method is called with any two integer arguments, it should return 5. Finally, we have acted by calling the “Add” method with the arguments 2 and 3, and then asserted that the result is equal to 5.

Let’s write another test method to test the multiplication functionality of our calculator service:

[Fact]
public void Test_Multiplication()
{
// Arrange
A.CallTo(() => _calculatorService.Multiply(A<int>.Ignored, A<int>.Ignored))
.Returns(6);

// Act
var result = _calculatorService.Multiply(2, 3);

// Assert
Assert.Equal(6, result);
}

In the above code, we have added a method called “Test_Multiplication” and decorated it with the “Fact” attribute. We have also arranged the test by using the FakeItEasy plugin to mock the “Multiply” method of the “ICalculatorService” interface. We have specified that when the “Multiply” method is called with any two integer arguments, it should return 6. Finally, we have acted by calling the “Multiply” method with the arguments 2 and 3, and then asserted that the result is equal to 6.

Mocking Database and Other Dependencies

Mocking database and other dependencies is also an important aspect of unit testing. Let’s say we have a class called “UserService” that depends on a database context. Here is how we can mock the database context using the FakeItEasy plugin:

public class UserServiceTests
{
private readonly MyDbContext _dbContext;
private readonly IUserService _userService;

public UserServiceTests()
{
_dbContext = A.Fake<MyDbContext>();
_userService = new UserService(_dbContext);
}

[Fact]
public void Test_GetUserById()
{
// Arrange
var user = new User { Id = 1, Name = "John" };
A.CallTo(() => _dbContext.Users.Find(A<int>.Ignored))
.Returns(user);

// Act
var result = _userService.GetUserById(1);

// Assert
Assert.Equal(user, result);
}
}

In the above code, we have created a class called “UserServiceTests” and added a field called “_dbContext” of type “MyDbContext.” We have also added a field called “_userService” of type “IUserService.” We have used the FakeItEasy plugin to mock the “MyDbContext” dependency. We have also created a test method called “Test_GetUserById” that tests the “GetUserById” method of the “UserService” class. We have arranged the test by using the FakeItEasy plugin to mock the “Find” method of the “Users” property of the “MyDbContext” class. We have specified that when the “Find” method is called with any integer argument, it should return a user object with an id of 1 and a name of “John.” Finally, we have acted by calling the “GetUserById” method with an argument of 1, and then asserted that the result is equal to the user object that we created.

Why FakeItEasy?

FakeItEasy is a powerful mocking library for .NET that allows developers to easily create fake objects and test their code in isolation. In this article, we will explore some of the different aspects of FakeItEasy and how to use them in various scenarios.

Creating Fakes

Creating fakes is the first step in using FakeItEasy. Fakes are objects that mimic the behavior of a real object, but are not the real object. Fakes can be used to replace real objects during testing, so that the behavior of the system under test can be isolated.

Here is an example of how to create a fake object using FakeItEasy:

var fakeObject = A.Fake<ISomeInterface>();

In the above code, we have created a fake object of type “ISomeInterface.” The “A.Fake” method is a static method of the “FakeItEasy” class that takes a type parameter and returns a fake object of that type.

Configuring Fakes

Configuring fakes is the next step in using FakeItEasy. Once a fake object has been created, its behavior can be configured to suit the needs of the test.

Here is an example of how to configure a fake object using FakeItEasy:

A.CallTo(() => fakeObject.SomeMethod()).Returns("some value");

In the above code, we have configured the “SomeMethod” method of the fake object to return the string “some value” when it is called.

Asserting Calls

Asserting calls is another important aspect of using FakeItEasy. When a fake object is used during testing, it is important to ensure that the system under test is making the correct calls to the fake object.

Here is an example of how to assert calls using FakeItEasy:

A.CallTo(() => fakeObject.SomeMethod()).MustHaveHappened();

In the above code, we have asserted that the “SomeMethod” method of the fake object must have been called at least once during the test.

Argument Matchers

Argument matchers are another feature of FakeItEasy that can be used to configure fakes. Argument matchers allow developers to specify how an argument should be matched when a method is called on a fake object.

Here is an example of how to use argument matchers with FakeItEasy:

A.CallTo(() => fakeObject.SomeMethod(A<int>.That.IsGreaterThan(5))).Returns("some value");

In the above code, we have configured the “SomeMethod” method of the fake object to return the string “some value” only when it is called with an integer argument greater than 5.

Mocking Database connection using FakeItEasy

Mocking a database connection is an important aspect of unit testing, as it allows developers to isolate the behavior of their code from the actual database. By using FakeItEasy to mock the database connection, developers can create unit tests that are fast, reliable, and repeatable. In this section, we will explore how to use FakeItEasy to mock a database connection.

To demonstrate how to use FakeItEasy to mock a database connection, let’s consider the following scenario. Suppose we have a class named “CustomerService” that interacts with a database to perform CRUD (Create, Read, Update, Delete) operations on customer data. The class has a constructor that takes an instance of a database connection as a parameter, and its methods make calls to the database connection to retrieve and manipulate data.

public class CustomerService
{
private readonly IDbConnection _dbConnection;

public CustomerService(IDbConnection dbConnection)
{
_dbConnection = dbConnection;
}

public IEnumerable<Customer> GetAllCustomers()
{
// Implementation details
}

public void AddCustomer(Customer customer)
{
// Implementation details
}

// More methods for updating and deleting customers
}

To test the behavior of the “CustomerService” class, we need to create a fake database connection and configure it to return the appropriate data when its methods are called. Here’s how we can do that using FakeItEasy:

[TestMethod]
public void GetAllCustomers_Returns_All_Customers()
{
// Arrange
var fakeConnection = A.Fake<IDbConnection>();
var customerService = new CustomerService(fakeConnection);
var expectedCustomers = new List<Customer>
{
new Customer { Id = 1, FirstName = "John", LastName = "Doe" },
new Customer { Id = 2, FirstName = "Jane", LastName = "Doe" }
};
A.CallTo(() => fakeConnection.Query<Customer>("SELECT * FROM Customers"))
.Returns(expectedCustomers);

// Act
var actualCustomers = customerService.GetAllCustomers();

// Assert
CollectionAssert.AreEqual(expectedCustomers, actualCustomers.ToList());
}

In the above code, we have created a fake database connection using FakeItEasy and passed it to an instance of the “CustomerService” class. We have then configured the “Query” method of the fake connection to return a list of customer objects when called with the SQL query “SELECT * FROM Customers”. Finally, we have called the “GetAllCustomers” method of the “CustomerService” class and asserted that it returns the expected list of customer objects.

By using FakeItEasy to mock the database connection, we have created a unit test that isolates the behavior of the “CustomerService” class from the actual database. This makes the test fast, reliable, and repeatable, and allows us to catch any issues early in the development process.

Benefits of using FakeItEasy

FakeItEasy offers many benefits over other mocking libraries. Here are some of the key benefits of using FakeItEasy:

  1. Fluent and intuitive syntax: FakeItEasy has a fluent and intuitive syntax that is easy to understand and use. This makes it easy to write and maintain unit tests, even for developers who are new to mocking.
  2. Support for mocking interfaces and classes: FakeItEasy supports mocking both interfaces and classes, which makes it a versatile mocking library for .NET Core.
  3. Built-in support for mocking static methods and properties: FakeItEasy has built-in support for mocking static methods and properties, which is not available in all mocking libraries. This can be especially useful when testing legacy code that uses static methods or properties.
  4. Automatic mocking of dependencies: FakeItEasy can automatically create fake objects for dependencies of the object being tested, which can simplify the setup of unit tests and make them easier to read.
  5. Integration with testing frameworks: FakeItEasy integrates seamlessly with popular .NET Core testing frameworks like XUnit and NUnit, making it easy to incorporate mocking into your existing test suite.

Overall, the combination of its fluent and intuitive syntax, support for mocking interfaces and classes, built-in support for mocking static methods and properties, automatic mocking of dependencies, and integration with testing frameworks makes FakeItEasy a powerful and flexible mocking library for .NET Core.

Conclusion

In this article, we have discussed how to write unit tests using XUnit and mock dependencies using the FakeItEasy plugin in .NET Core C#. We have also provided examples of how to write unit tests for addition and multiplication functionality, as well as how to mock a database context in a unit test. Writing unit tests is an essential part of software development, and using XUnit and FakeItEasy makes it easier to write effective unit tests in .NET Core.

FakeItEasy is a powerful mocking library for .NET that can be used to create fake objects, configure their behavior, assert calls, and use argument matchers. These features make it easy for developers to write effective unit tests that isolate the behavior of the system under test. By using FakeItEasy in their tests, developers can be confident that their code is working correctly and that any issues can be caught early in the development process.

--

--