簡體   English   中英

使用NUnit / NSubstitute / AutoFixture和InsightDatabase模擬DbConnection

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM