【发布时间】:2018-07-24 20:32:28
【问题描述】:
我正在尝试按照@NKosi Here 的建议对我的服务层进行单元测试。我能够通过实现实际的工厂实现成功地进行集成测试,而无需模拟任何东西,但无法进行单元测试(通过模拟 IDbConnection 和我的 SQL 连接工厂类),因为 Dapper 查询执行失败并出现错误“对象未设置为一个对象的实例”。
我的 IDbConnection 工厂及其实现如下
public interface IDbConnectionFactory
{
IDbConnection CreateConnection();
}
public class ConnectionSetings
{
public string Name { get; set; }
}
public class SqlConnectionFactory : IDbConnectionFactory
{
private readonly ConnectionSetings connectionSettings;
public SqlConnectionFactory(ConnectionSetings connectionSettings)
{
this.connectionSettings = connectionSettings;
}
public IDbConnection CreateConnection()
{
return new SqlConnection(connectionSettings.Name);
}
}
XUnit 测试如下
[Fact]
public void Get_RestaurantById_ReturnsRestaurant()
{
//Arrange
var connection = new Mock<IDbConnection>();
var dbConnectionFactory = new Mock<IDbConnectionFactory>();
dbConnectionFactory.Setup(x => x.CreateConnection()).Returns(connection.Object);
//Act
var result = new SqlRestaurantDataCL(dbConnectionFactory.Object).Get(1);
//Assert
result.Name.Equals("Test Name 1");
//Assert.Equal("Test Name 1", result.Name);
}
服务层如下
public class SqlRestaurantDataCL : IRestaurantDataCL
{
private readonly IDbConnectionFactory factory;
public SqlRestaurantDataCL(IDbConnectionFactory factory)
{
this.factory = factory;
}
public Restaurant Get(int id)
{
using (var connection = factory.CreateConnection())
{
var selectSql = @"SELECT * From Restaurants Where Id = @Id";
var restaurant = connection.QuerySingleOrDefault<Restaurant>(selectSql, new
{
id
});
return restaurant;
}
}
}
以下是错误截图
【问题讨论】:
-
这里的问题是,在我看来,设置模拟连接以安全导航
QuerySingleOrDefaultDapper 扩展所需的模拟量并不值得头疼。这是试图模拟你不拥有或控制的代码的一个主要问题,比如 3rd 方依赖项,在这种情况下是 Dapper -
我个人会在我的代码和扩展之间添加另一层抽象来解决耦合问题。
-
你的意思是某种业务逻辑层,它会实例化数据库连接并调用数据服务层,这样我就可以通过模拟业务逻辑层来通过 dapper 障碍?
-
基本上不是 dapper 扩展的包装器,它允许您在没有所有连锁反应的情况下模拟行为
-
但我个人不会对
SqlRestaurantDataCL进行单元测试,因为我认为它是直接与数据源交互的最低级别之一。我会专注于依赖IRestaurantDataCL的内容并为这些测试模拟它
标签: unit-testing asp.net-core-2.0 dapper xunit xunit.net