Mock testing in C# .NET

Lucas Andrade
3 min readSep 11, 2023

--

What is mocking?

In software development, it is important to write unit tests for every feature. Unit tests are small, isolated tests that verify the correctness of a specific unit of code.

This helps to ensure that the code works correctly and that any changes to the code do not break existing functionality.

This is where mocking comes in. Mocking is a technique that allows us to create fake objects that simulate the behavior of real objects.

Why use mock?

One challenge in writing unit tests is that the code often depends on other services or components.

For example, a web application might depend on a database or a messaging service. To prevent coupling, these dependencies should be encapsulated in separate classes.

If we want to test the code in isolation, we need to find a way to simulate the behavior of these dependencies.

Mocking allows us to isolate the code from its dependencies and focus on testing the code itself.

How do I use mock?

Lets supose we have a service that tells us if the user is an admin.

public class UserService
{
private readonly IUserRepository userRepository;

public UserService(IUserRepository userRepository)
{
this.userRepository = userRepository;
}

public bool IsUserAdmin(int userId)
{
var user = userRepository.GetUserById(userId);
return user != null && user.Role == "Admin";
}
}

For the implementation, we will be using the NSubstitute and XUnit packages.

In order to write our test, we only want to test the UserService, and not its dependencies. So we will mock the IUserRepository with Substitute.For<IUserRepository>() and manipulate its results, like this:

using NSubstitute;
using Xunit;

public class UserServiceTests
{

private readonly IUserRepository _userRepository = Substitute.for<IUserRepository>();

public UserServiceTests() {
UserService userService = new UserService(_userRepository);
}

[Fact]
public void IsUserAdmin_ReturnsTrue_WhenUserIsAdmin()
{
// Arrange
var user = new User { Id = 1, Role = "Admin" };
_userRepository.GetUserById(userId).Returns(user);

// Act
bool result = userService.IsUserAdmin(userId);

// Assert
Assert.True(result);
}

[Fact]
public void IsUserAdmin_ReturnsFalse_WhenUserIsNotAdmin()
{
// Arrange
var user = new User { Id = 2, Role = "User" };
userRepository.GetUserById(userId).Returns(user);

// Act
bool result = userService.IsUserAdmin(userId);

// Assert
Assert.False(result);
}
}

What can I do with mock?

Mocking can be used to test a wide variety of scenarios. Here are a few examples:

  • We can mock a database to return different sets of data to test how the code handles different data.
  • We can mock a dependency to throw an exception to test how the code handles those exceptions.
  • We can mock a method to verify that it was called with the correct parameters to test how the code calls its dependencies.

Here are some code examples:

  • To test a method that throws an exception, we can mock the dependency using the method ReturnsNull() and throw the exception.
// This method throws an exception if the user is not found.
public void DeleteUser(int userId)
{
var user = _userRepository.GetUserById(userId);
if (user is null)
{
throw new UserNotFoundException();
}

_userRepository.DeleteUser(userId);
}

// To test this method, we can mock the user repository and throw an exception if the user is not found.
// For example, we can mock the user repository to throw an exception if the user with ID 1 is not found.

[Fact]
public void DeleteUser_ThrowsExceptionIfUserNotFound()
{
// Arrange
_userRepository.GetUserById(1).ReturnsNull();

// Act
var exception = Assert.Throws<UserNotFoundException>(() => DeleteUser(1));

// Assert
Assert.Equal("User with ID 1 not found", exception.Message);
}
  • To test a method that calls another method, we can mock the first method and have it verify that the second method was called with the correct parameters, using the method Received().
[Fact]
public void DeleteUser_VerifiesThatDeleteUserRepositoryWasCalled()
{
// Arrange
var user = new User { Id = 1 };
_userRepository.GetUserById(1).Returns(user);

// Act
userService.DeleteUser(1);

// Assert
_userRepository.Received(1).DeleteUser(userId);
}
  • The oposite of the test could also be done, by not returning a user, and using the method DidNotReceive() on the _userRepository mock.

Conclusion

Mocking is a powerful technique that can be used to test code in a variety of ways. By understanding how to use mocking, you can write more reliable and robust code.

--

--