Unit Testing in C#: MSTest, NUnit, xUnit, Moq Mocking & Best Practices

1. Unit Testing in C#

Q: What is unit testing in C#?

Unit testing is the process of testing individual units (e.g., methods, classes) of code in isolation to ensure they work as expected. In C#, unit testing is typically performed using frameworks like MSTest, NUnit, or xUnit, which provide attributes and assertions to define and validate test cases.

Q: Why is unit testing important?

Q: How does unit testing in C# differ from C/C++?

2. Writing Test Cases with MSTest/NUnit/xUnit

Q: What are MSTest, NUnit, and xUnit?

These are popular C# unit testing frameworks:

Q: How do you write test cases in these frameworks?

Test cases are written by:

Q: What are the key differences between MSTest, NUnit, and xUnit?

Q: Can you give an example of writing test cases with MSTest, NUnit, and xUnit?

Below is an example testing a simple Calculator class using all three frameworks.

using System;

// Production code
public class Calculator
{
    public int Add(int a, int b) => a + b;
    public int Divide(int a, int b)
    {
        if (b == 0) throw new DivideByZeroException();
        return a / b;
    }
}

#if MSTEST
using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class CalculatorTests_MSTest
{
    [TestMethod]
    public void Add_ShouldReturnSum()
    {
        Calculator calc = new Calculator();
        int result = calc.Add(3, 5);
        Assert.AreEqual(8, result);
    }

    [TestMethod]
    [ExpectedException(typeof(DivideByZeroException))]
    public void Divide_ByZero_ShouldThrow()
    {
        Calculator calc = new Calculator();
        calc.Divide(10, 0);
    }
}
#endif

#if NUNIT
using NUnit.Framework;

[TestFixture]
public class CalculatorTests_NUnit
{
    [Test]
    public void Add_ShouldReturnSum()
    {
        Calculator calc = new Calculator();
        int result = calc.Add(3, 5);
        Assert.AreEqual(8, result);
    }

    [Test]
    public void Divide_ByZero_ShouldThrow()
    {
        Calculator calc = new Calculator();
        Assert.Throws<DivideByZeroException>(() => calc.Divide(10, 0));
    }
}
#endif

#if XUNIT
using Xunit;

public class CalculatorTests_xUnit
{
    [Fact]
    public void Add_ShouldReturnSum()
    {
        Calculator calc = new Calculator();
        int result = calc.Add(3, 5);
        Assert.Equal(8, result);
    }

    [Fact]
    public void Divide_ByZero_ShouldThrow()
    {
        Calculator calc = new Calculator();
        Assert.Throws<DivideByZeroException>(() => calc.Divide(10, 0));
    }
}
#endif

Output (example, all frameworks):

Note: The example uses preprocessor directives (#if) to separate frameworks for clarity. In practice, choose one framework per project.

3. Mocking Dependencies

Q: What is mocking in unit testing?

Mocking is the process of creating fake implementations of dependencies (e.g., services, databases) to isolate the unit being tested. Mocks simulate behavior and return predefined results, allowing tests to focus on the logic of the unit without relying on external systems.

Q: Why is mocking important?

Q: What libraries are used for mocking in C#?

Popular mocking libraries include:

Q: Can you give an example of mocking dependencies with Moq in a unit test?

Below is an example using Moq with xUnit to test a UserService that depends on an ILogger. The test mocks the logger to isolate the service’s logic.

using System;
using Moq;
using Xunit;

// Interface and service
public interface ILogger
{
    void Log(string message);
}

public class UserService
{
    private readonly ILogger _logger;

    public UserService(ILogger logger)
    {
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    public string ProcessUser(string userName)
    {
        if (string.IsNullOrEmpty(userName))
        {
            _logger.Log("Invalid user name provided.");
            throw new ArgumentException("User name cannot be empty.");
        }
        _logger.Log($"Processing user: {userName}");
        return $"Processed {userName}";
    }
}

// xUnit tests with Moq
public class UserServiceTests
{
    private readonly Mock<ILogger> _loggerMock;
    private readonly UserService _userService;

    public UserServiceTests()
    {
        _loggerMock = new Mock<ILogger>();
        _userService = new UserService(_loggerMock.Object);
    }

    [Fact]
    public void ProcessUser_ValidName_ReturnsProcessedMessage()
    {
        // Arrange
        string userName = "Krishna";
        _loggerMock.Setup(l => l.Log(It.IsAny<string>())).Verifiable();

        // Act
        string result = _userService.ProcessUser(userName);

        // Assert
        Assert.Equal($"Processed {userName}", result);
        _loggerMock.Verify(l => l.Log($"Processing user: {userName}"), Times.Once());
    }

    [Fact]
    public void ProcessUser_EmptyName_ThrowsArgumentException()
    {
        // Arrange
        _loggerMock.Setup(l => l.Log(It.IsAny<string>())).Verifiable();

        // Act & Assert
        var exception = Assert.Throws<ArgumentException>(() => _userService.ProcessUser(""));
        Assert.Equal("User name cannot be empty.", exception.Message);
        _loggerMock.Verify(l => l.Log("Invalid user name provided."), Times.Once());
    }
}

Q: How does mocking in C# differ from C/C++?

Q: What are common mistakes with unit testing and mocking in C#?

Unit Testing:

Mocking:

Q: What are best practices for unit testing and mocking in C#?

Unit Testing:

Mocking:

General: