1

I have an application working with Employees. So I decided to use Mock to test the Employee Repository.

Here is my method to test

class EmployeesRepository : IEmployeeRepository ...

public IEnumerable<Employee> GetAllEmployees()
{
    List<Employee> list = new List<Employee>();

    try
    {           
        string connectionString = _secureConfig.Value.MyDbSetting;
        
        string sql = "select id, firstname, lastname, entrydate, email from empoyees";
        using SqlConnection connection = new SqlConnection(connectionString);
        using SqlCommand command = new SqlCommand(sql, connection);
        connection.Open();

        using SqlDataReader reader = command.ExecuteReader();
        while (reader.Read())
        {
            list.Add(new Employee
            {
                Id = reader.GetString(0),
                FirstName = reader.GetString(1),
                LastName = reader.GetString(2),
                EntryDate = reader.GetDateTime(3),
                Email = (reader.IsDBNull(4) ? null : reader.GetString(4))
            });
        }
    }
    catch (Exception ex)
    {
        _log.LogError(ex, "An error is caught when getting all employees");
    }
    return list;
}

My question is what and how to mock in this case... should I mock the datareader, or just only the ExecutReader method... please give an advice from where to start the test of such methods with direct access to the DB.

0

1 Answer 1

2

Tight coupling to implementation details/concerns make this difficult to unit test in isolation.

Refactor to depend on abstractions that can be mocked.

For example

class EmployeesRepository : IEmployeeRepository {
    private readonly IDbConnectionFactory dbConnectionFactory;
    //...

    public EmployeesRepository(IDbConnectionFactory dbConnectionFactory /*,...*/) {
        this.dbConnectionFactory = dbConnectionFactory;

        //...
    }

    public IEnumerable<Employee> GetAllEmployees() {
        List<Employee> list = new List<Employee>();

        try {
            string connectionString = _secureConfig.Value.MyDbSetting;
            string sql = "select id, firstname, lastname, entrydate, email from empoyees";

            using (IDbConnection connection = dbConnectionFactory.CreateConnection(connectionString)) {
                using (IDbCommand command = connection.CreateCommand()) {
                    command.CommandText = sql;

                    connection.Open();

                    using (IDataReader reader = command.ExecuteReader()) {
                        while (reader.Read()) {
                            list.Add(new Employee {
                                Id = reader.GetString(0),
                                FirstName = reader.GetString(1),
                                LastName = reader.GetString(2),
                                EntryDate = reader.GetDateTime(3),
                                Email = (reader.IsDBNull(4) ? null : reader.GetString(4))
                            });
                        }
                    }
                }
            }
        } catch (Exception ex) {
            _log.LogError(ex, "An error is caught when getting all employees");
        }
        return list;
    }
}

Where IDbConnectionFactory is defined as

public interface IDbConnectionFactory {
    ///<summary>
    ///  Creates a connection based on the given database name or connection string.
    ///</summary>
    IDbConnection CreateConnection(string nameOrConnectionString);
}

The runtime implementation that will be registered with your DI container would look like this

class SqlConnectionFactory : IDbConnectionFactory {
    public IDbConnection CreateConnection(string nameOrConnectionString) {
        return new SqlConnection(nameOrConnectionString);
    }
}


//...

services.AddSingleton<IDbConnectionFactory, SqlConnectionFactory>();

//...

For testing the above, the abstractions can be mocked as needed

public void ShouldGetAllEmployees() {
    //Arrange

    var readerMock = new Mock<IDataReader>();
    //...setup reader members as needed
    
    var commandMock = new Mock<IDbCommand>();
    commandMock.Setup(m => m.ExecuteReader())
        .Returns(readerMock.Object);

    var connectionMock = new Mock<IDbConnection>();
    connectionMock.Setup(m => m.CreateCommand())
        .Returns(commandMock.Object);
    //..Setup...

    var connectionFactoryMock = new Mock<IDbConnectionFactory>();
    connectionFactoryMock
        .Setup(m => m.CreateConnection(It.IsAny<string>()))
        .Returns(connectionMock.Object);

    EmployeesRepository sut = new EmployeesRepository(connectionFactoryMock.Object);

    //Act
    IEnumerable<Employee> actual = sut.GetAllEmployees();

    //Assert
    //...assert desired behavior
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.