我在测试 IDBconnection 模拟时遇到了麻烦,因为它给了我一个空数据库。 请注意,对于我的 IConfiguration,而不是模拟,我创建了一个 appsettingstest.json,其中包含与数据库的连接字符串以用于测试目的。 但对于 IDbConnection,我不知道如何传递 ConnectionString,因为这是我使用 Dapper 的第一个项目。
这是我的处理程序构造函数:
IDbConnection _dbConnection;
Context _context;
SigSettings _settings;
public SignalsHandler(IConfiguration configuration, IDbConnection connection)
{
_dbConnection = connection;
_settings = configuration.GetSection("SigSettings").Get<SigSettings>();
if (_settings == null)
_settings = new SigSettings();
}
这是我的测试类构造函数:
private Mock<IDbConnection> _connection = new Mock<IDbConnection>();
private readonly SignalsHandler _handler;
private readonly IConfiguration _configuration;
private readonly SigSettings _settings;
public SignalsTest()
{
_configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile(@"appsettingstest.json", false, false)
.AddEnvironmentVariables()
.Build();
_connection.Setup(x => x.ConnectionString).Returns(path);
_settings = _configuration.GetSection("SigSettings").Get<SigSettings>();
if (_settings == null)
_settings = new SigSettings();
_handler = new SignalsHandler(_configuration, _connection.Object);
}
这有效,但是当我执行测试方法时,例如这个虚拟测试:
public void CanGetAllSignals()
{
_connection.Setup(x => x.ConnectionString);
var result = _handler.GetSignals();
Assert.IsNotNull(result);
}
它调用这个方法:
public async Task<List<SignalsDTO>> GetSignals()
{
var res = new List<SignalsDTO>();
var sigList = await _dbConnection.QueryAsync<SigSignal>(_settings.Signals);
foreach (var s in sigList)
res.Add(new SignalsDTO(){IDTag = s.IDTag, Name = s.Name});
return res;
}
这就是我遇到问题的地方,因为它在这一行给了我一个
NullReferenceException: 'Object reference not set to an instance of an object'
:var sigList = await _dbConnection.QueryAsync<SigSignal>(_settings.Signals);
,如果你查看_dbConnection
,你可以看到它是一个模拟对象,并且数据库为空,所以这就是失败。
我该如何解决这个问题?我试图嘲笑它,但也许这不是最好的方法。
问题在于该方法是扩展方法,而不是对象本身的方法。根据您使用的模拟框架,可能还有一种方法可以模拟扩展方法(更新:kuldeep 的答案显示了一种潜在的方法)。
但是,由于您专门使用 Dapper,因此您可以查看现成的解决方案,例如 https://github.com/UnoSD/Moq.Dapper for Moq。
它允许您编写如下测试:
[Test]
public async Task QueryAsyncGeneric()
{
var connection = new Mock<DbConnection>();
var expected = new[] { 7, 77, 777 };
connection.SetupDapperAsync(c => c.QueryAsync<int>(It.IsAny<string>(), null, null, null, null))
.ReturnsAsync(expected);
var actual = (await connection.Object.QueryAsync<int>("", null, null, null, null)).ToList();
Assert.That(actual.Count, Is.EqualTo(expected.Length));
Assert.That(actual, Is.EquivalentTo(expected));
}
您必须像下面这样设置 QueryAsync 的期望
this._connection
.Setup(s => s.QueryAsync<SigSignal>(
It.IsAny<Signals>())
.ReturnsAsync(yourFakeSigListWithTestData);
在 It.IsAny() 中,你应该传入正确的对象,我只是在这里放置信号,但理想情况下它必须是 _settings.Signals 类型的东西
编辑:
另一种可能的方法是你自己制作一个存根。实现 IDbConnection,然后将该存根作为依赖项传递,您可以在这个存根中基本上提供您自己的 IDbConnection 方法的实现,这些方法实际上在您的测试路径中被调用。
为了存根 IDbConnection 看一下这个 answer
我建议将此 Moq 扩展用于
Dapper
方法:https://github.com/UnoSD/Moq.Dapper。它提供了对许多方法的方便模拟,例如
QueryFirstOrDefault()
QueryAsync()
Query()
ExecuteScalar()
ExecuteScalarAsync()
这是随附的示例
DapperTest.cs
:
[Test]
public void QueryAsyncGeneric()
{
var connection = new Mock<DbConnection>();
var expected = new[] { 7, 77, 777 };
connection
.SetupDapperAsync(c => c.QueryAsync<int>(It.IsAny<string>(), null, null, null, null))
.ReturnsAsync(expected);
var actual = connection.Object
.QueryAsync<int>("")
.GetAwaiter()
.GetResult()
.ToList();
Assert.That(actual.Count, Is.EqualTo(expected.Length));
Assert.That(actual, Is.EquivalentTo(expected));
}