Tuesday, March 4, 2025

NUnit Testing Moq : Explanation

This code is a unit test class written in C# using the NUnit testing framework and the Moq library for mocking dependencies. It tests the functionality of an EmployeeService class, which interacts with an IEmployeeRepository to perform operations like retrieving, adding, and querying employee data. Below is a detailed explanation and interpretation of the code, with comments added for clarity.



Class Declaration and Setup

csharp
Copy
[TestFixture] // Marks the class as a test class
public class EmployeeServiceTests // Marks the class as a test class
{
    private Mock<IEmployeeRepository> _employeeRepositoryMock;
    private IEmployeeService _employeeService;

  • [TestFixture]: This attribute indicates that the class contains unit tests.

  • EmployeeServiceTests: The name of the test class, which typically tests the EmployeeService or related functionality.

  • _employeeRepositoryMock: A mock object of type IEmployeeRepository, created using the Moq library. This allows simulating the behavior of the repository without interacting with a real database.

  • _employeeService: An instance of IEmployeeService, which is the system under test (SUT).



Setup Method

csharp
Copy
[SetUp] // Runs before each test
public void Setup()
{
    _employeeRepositoryMock = new Mock<IEmployeeRepository>();
    _employeeService = new EmployeeServiceImpl(_employeeRepositoryMock.Object);
}

  • [SetUp]: This attribute marks the method as a setup method, which runs before each test method in the class.

  • Setup(): Initializes the mock object (_employeeRepositoryMock) and creates an instance of EmployeeServiceImpl (the implementation of IEmployeeService), injecting the mock repository as a dependency.



Helper Method: GetSampleEmployees

csharp
Copy
// Helper method to generate sample data
private List<Employee> GetSampleEmployees()
{
    return new List<Employee>()
    {
        new Employee
        {
            Id = 1,
            Name = "John Doe",
            Gender = "Male",
            Designation = "Software Engineer",
            Salary = 50000
        },
        new Employee
        {
            Id = 2,
            Name = "Jane Smith",
            Gender = "Female",
            Designation = "Marketing Manager",
            Salary = 45000
        }
    };
}
  • GetSampleEmployees(): A helper method that returns a list of sample Employee objects. This is used to simulate data returned by the repository.



Test Method: GetAllEmployees_ShouldReturnCorrectListOfEmployees

csharp
Copy
[Test]
public void GetAllEmployees_ShouldReturnCorrectListOfEmployees()
{
    // Arrange
    var sampleEmployees = GetSampleEmployees();
    _employeeRepositoryMock.Setup(repo => repo.GetAllEmployees()).Returns(sampleEmployees);

    // Act
    var employees = _employeeService.GetAllEmployees().ToList();

    // Assert
    employees.Should().NotBeNullOrEmpty();
    employees.Should().HaveCount(3); // Verify the correct count
    employees.First().Name.Should().Be(sampleEmployees.First().Name); // Verify the first employee's name
}

  • Purpose: Tests the GetAllEmployees method of EmployeeService.

  • Arrange:

    • Configures the mock repository to return the sample list of employees when GetAllEmployees() is called.

  • Act:

    • Calls the GetAllEmployees() method on the _employeeService and converts the result to a list.

  • Assert:

    • Uses FluentAssertions (Should()) to verify:

      • The returned list is not null or empty.

      • The list contains 3 employees (this assertion is incorrect because the sample data only has 2 employees).

      • The first employee's name matches the expected name from the sample data.



Test Method: GetEmployeeById_ShouldReturnCorrectEmployee

csharp
Copy
[Test]
public void GetEmployeeById_ShouldReturnCorrectEmployee()
{
    // Arrange
    int empId = 1;
    var sampleEmployees = GetSampleEmployees();
    var expectedEmployee = sampleEmployees.FirstOrDefault(e => e.Id == empId);

    _employeeRepositoryMock.Setup(repo => repo.GetEmployeeById(empId)).Returns(expectedEmployee);

    // Act
    var actualEmployee = _employeeService.GetEmployeeById(empId);

    // Assert
    actualEmployee.Should().NotBeNull();
    actualEmployee.Id.Should().Be(expectedEmployee.Id);
    actualEmployee.Name.Should().Be("John Doe");
    actualEmployee.Designation.Should().Be(expectedEmployee.Designation);
}

  • Purpose: Tests the GetEmployeeById method of EmployeeService.

  • Arrange:

    • Sets up the mock repository to return the employee with Id = 1 when GetEmployeeById(empId) is called.

  • Act:

    • Calls the GetEmployeeById(empId) method on the _employeeService.

  • Assert:

    • Verifies that the returned employee is not null and that its properties (IdNameDesignation) match the expected values.



Test Method: AddEmployee_ShouldCallRepositoryAddMethodOnce

csharp
Copy
[Test]
public void AddEmployee_ShouldCallRepositoryAddMethodOnce()
{
    // Arrange
    var newEmployee = new Employee
    {
        Id = 3,
        Name = "Mark John",
        Gender = "Male",
        Designation = "Senior Manager",
        Salary = 75000
    };

    // Act
    _employeeService.AddEmployee(newEmployee);

    // Assert
    _employeeRepositoryMock.Verify(repo => repo.AddEmployee(newEmployee), Times.Never);
}

  • Purpose: Tests the AddEmployee method of EmployeeService.

  • Arrange:

    • Creates a new Employee object with specific properties.

  • Act:

    • Calls the AddEmployee(newEmployee) method on the _employeeService.

  • Assert:

    • Verifies that the AddEmployee method on the mock repository was never called (Times.Never). This is likely a bug because the test name suggests it should verify that the method was called once.



Test Method: GetEmployeeById_ShouldReturnNullForInvalidId

csharp
Copy
[Test]
public void GetEmployeeById_ShouldReturnNullForInvalidId()
{
    // Arrange
    int invalidEmpId = 999; // An ID that does not exist in the sample data
    _employeeRepositoryMock.Setup(repo => repo.GetEmployeeById(invalidEmpId)).Returns((Employee)null);

    // Act
    var result = _employeeService.GetEmployeeById(invalidEmpId);

    // Assert
    result.Should().BeNull();
}

  • Purpose: Tests the GetEmployeeById method of EmployeeService for an invalid employee ID.

  • Arrange:

    • Sets up the mock repository to return null for an invalid employee ID (999).

  • Act:

    • Calls the GetEmployeeById(invalidEmpId) method on the _employeeService.

  • Assert:

    • Verifies that the result is null, which is the expected behavior when an invalid ID is provided.


Issues in the Code

  1. Incorrect Assertion in GetAllEmployees_ShouldReturnCorrectListOfEmployees:

    • The sample data contains 2 employees, but the test asserts that the list should have 3 employees.

  2. Incorrect Assertion in AddEmployee_ShouldCallRepositoryAddMethodOnce:

    • The test name suggests verifying that the repository method is called once, but the assertion checks for Times.Never.

  3. Hardcoded Test Data:

    • The test data is hardcoded, which may not cover all edge cases.


Suggestions for Improvement

  1. Fix the assertions in GetAllEmployees_ShouldReturnCorrectListOfEmployees to match the sample data.

  2. Fix the assertion in AddEmployee_ShouldCallRepositoryAddMethodOnce to verify Times.Once.

  3. Use data-driven tests (e.g., [TestCase]) to test multiple scenarios.

  4. Add more edge case tests (e.g., empty repository, null inputs).


This code is a good starting point for unit testing, but it requires some fixes and enhancements to ensure it is robust and reliable. 

No comments:

Post a Comment