[英]Mocking a DbConnection with NUnit/NSubstitute/AutoFixture and InsightDatabase
我們正在使用Nunit,NSubstitute和AutoFixture來測試基於Insight數據庫之上的存儲庫類...
[TestFixture]
public class CalculationResultsRepositoryTests
{
private IFixture _fixture;
private IDbConnection _connection;
private CalculationResultsRepository _calculationResultsRepository;
[SetUp]
public void Setup()
{
_fixture = new Fixture().Customize(new AutoConfiguredNSubstituteCustomization());
_connection = _fixture.Freeze<IDbConnection>();
_calculationResultsRepository = _fixture.Create<CalculationResultsRepository>();
}
[Test]
public void TestReturnsPagedCalculationResults()
{
//Arrange
var financialYear = _fixture.Create<int>();
var pagedResults = _fixture.Create<PagedResults<ColleagueCalculationResult>>();
_connection.QueryAsync(Arg.Any<string>(), Arg.Any<object>(), Arg.Any<IQueryReader<PagedResults<ColleagueCalculationResult>>>()).Returns(pagedResults);
//Act
var result = _calculationResultsRepository.PagedListAsync(financialYear);
//Assert
Assert.IsInstanceOf<PagedResults<ColleagueCalculationResult>>(result);
}
}
但是,在運行測試時,我們看到以下異常:
System.Reflection.TargetInvocationException:調用的目標引發了異常。 ----> NSubstitute.Exceptions.UnexpectedArgumentMatcherException:參數匹配器(Arg.Is,Arg.Any)僅應用於代替成員參數。 不要在Returns()語句或成員調用之外的任何其他地方使用。 正確使用:sub.MyMethod(Arg.Any())。Returns(“ hi”)錯誤使用:sub.MyMethod(“ hi”)。Returns(Arg.Any())
我們對於如何解決這個問題有點茫然,但是,猜測似乎與將返回類型定義為參數中泛型的QueryAsync()擴展方法的特定重載有關。 InsightDatabase:
public static Task<T> QueryAsync<T>(this IDbConnection connection, string sql, object parameters, IQueryReader<T> returns, CommandType commandType = CommandType.StoredProcedure, CommandBehavior commandBehavior = CommandBehavior.Default, int? commandTimeout = default(int?), IDbTransaction transaction = null, CancellationToken? cancellationToken = default(CancellationToken?), object outputParameters = null);
有人知道如何成功模擬嗎?
為了完整起見,我們嘗試替代的方法調用是這樣的:
var results = await _connection.QueryAsync("GetCalculationResults", new { FinancialYearId = financialYearId, PageNumber = pageNumber, PageSize = pageSize },
Query.ReturnsSingle<PagedResults<ColleagueCalculationResult>>()
.ThenChildren(Some<ColleagueCalculationResult>.Records));
我根據您的測試做了一些更改。 看看是否有幫助。
[Test]
public async Task TestReturnsPagedCalculationResults()
{
//Arrange
var financialYear = _fixture.Create<int>();
var pagedResults = _fixture.Create<PagedResults<ColleagueCalculationResult>>();
_connection.QueryAsync(null, null, null).ReturnsForAnyArgs(Task.FromResult(pagedResults));
//Act
var result = await _calculationResultsRepository.PagedListAsync(financialYear);
//Assert
Assert.IsInstanceOf<PagedResults<ColleagueCalculationResult>>(result);
}
這可能不是最好的方法,但是由於您無法模擬擴展方法,而且我沒有時間編寫Insight的測試實現,因此目前看來這是可以接受的解決方案...
創建的IInsightDatabase接口:
public interface IInsightDatabase
{
Task<T> QueryAsync<T>(string sql, object parameters, IQueryReader<T> returns, CommandType commandType = CommandType.StoredProcedure, CommandBehavior commandBehavior = CommandBehavior.Default, int? commandTimeout = default(int?), IDbTransaction transaction = null, CancellationToken? cancellationToken = default(CancellationToken?), object outputParameters = null);
}
創建了IInsightDatabase的具體實現:
public class InsightDatabase : IInsightDatabase
{
private readonly IDbConnection _connection;
public InsightDatabase(IDbConnection connection)
{
_connection = connection;
}
public async Task<T> QueryAsync<T>(string sql, object parameters, IQueryReader<T> returns, CommandType commandType = CommandType.StoredProcedure, CommandBehavior commandBehavior = CommandBehavior.Default, int? commandTimeout = default(int?), IDbTransaction transaction = null, CancellationToken? cancellationToken = default(CancellationToken?), object outputParameters = null)
{
return await _connection.QueryAsync(sql, parameters, returns, commandType, commandBehavior, commandTimeout, transaction, cancellationToken, outputParameters);
}
}
現在,將具體實現注入到存儲庫類中,從而可以通過模擬IInsightDatabase進行測試:
private IFixture _fixture;
private IInsightDatabase _insightDatabase;
private CalculationResultsRepository _calculationResultsRepository;
[SetUp]
public void Setup()
{
_fixture = new Fixture().Customize(new AutoConfiguredNSubstituteCustomization());
_insightDatabase = _fixture.Freeze<IInsightDatabase>();
_calculationResultsRepository = _fixture.Create<CalculationResultsRepository>();
}
[Test]
public async Task PagedListAsync_ReturnsPagedResults()
{
//Arrange
var financialYearId = _fixture.Create<int>();
var pagedResults = _fixture.Create<PagedResults<ColleagueCalculationResult>>();
_insightDatabase.QueryAsync(Arg.Any<string>(), Arg.Any<object>(), Arg.Any<IQueryReader<PagedResults<ColleagueCalculationResult>>>()).Returns(pagedResults);
//Act
var result = await _calculationResultsRepository.PagedListAsync(financialYearId);
//Assert
result.Should().NotBeNull();
result.Should().BeOfType<PagedResults<ColleagueCalculationResult>>();
result.Should().Be(pagedResults);
}
塔達! 現在,該存儲庫類是可測試的,並且處理了IDbConnection,對擴展方法的調用以及所有其他棘手問題的Insights很好地被隱藏在一個雖然不可測試但應該很難打破的東西中。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.