【问题标题】:Mock Testing the SqlDataReader results模拟测试 SqlDataReader 结果
【发布时间】:2021-01-05 14:20:19
【问题描述】:

我有一个与员工合作的应用程序。所以我决定使用 Mock 来测试 Employee Repository。

这是我的测试方法

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;
}

我的问题是在这种情况下模拟什么以及如何模拟......我应该模拟数据读取器,还是只模拟 ExecutReader 方法......请提供建议,从哪里开始测试这些方法并直接访问数据库。

【问题讨论】:

    标签: .net .net-core integration-testing moq .net-core-3.1


    【解决方案1】:

    与实现细节/问题的紧密耦合使得这很难单独进行单元测试。

    重构依赖于可以模拟的抽象。

    例如

    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;
        }
    }
    

    IDbConnectionFactory 定义为

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

    将在您的 DI 容器中注册的运行时实现如下所示

    class SqlConnectionFactory : IDbConnectionFactory {
        public IDbConnection CreateConnection(string nameOrConnectionString) {
            return new SqlConnection(nameOrConnectionString);
        }
    }
    
    
    //...
    
    services.AddSingleton<IDbConnectionFactory, SqlConnectionFactory>();
    
    //...
    

    为了测试上述内容,可以根据需要模拟抽象

    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
    }
    

    【讨论】:

      猜你喜欢
      • 2021-10-16
      • 2016-02-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-12
      • 2020-05-05
      • 1970-01-01
      • 2016-05-14
      相关资源
      最近更新 更多