簡體   English   中英

單元測試 EF Core 3.1 異步方法

[英]Unit test EF Core 3.1 Async methods

我在一個沒有集成測試設置的項目上工作。

當我處理一些后端任務時,我需要對與數據庫交互相關的內容進行一些測試。

我使用 EF Core 3.1,因為它已經實現了存儲庫模式,所以我能夠為每個不同的實體創建擴展方法。

事情就在這里出現了。 每個 LINQ 查詢都被翻譯成純 SQL。 這也意味着查詢邏輯的行為必須與翻譯后的 SQL 代碼完全相同。

我已經檢查過並且我被允許針對非異步方法編寫單元測試,但是在異步方法的情況下,它似乎並不是那么簡單。

例如,我有以下擴展方法:

public static class PostcodeExclusionExtension
{

    public static bool CheckPostcodeIsSupported(this IQueryable<PostcodeExclusion> postcodeExclusion, int customerId, string postcode) =>
        !postcodeExclusion.Any(ApplyIsSupportedExpression(customerId, postcode));

    public static async Task<bool> CheckPostcodeIsSupportedAsync(this IQueryable<PostcodeExclusion> postcodeExclusion, int customerId, string postcode) =>
        !await postcodeExclusion.AnyAsync(ApplyIsSupportedExpression(customerId, postcode));

    private static Expression<Func<PostcodeExclusion, bool>> ApplyIsSupportedExpression(int customerId, string postcode)
    {
        postcode = postcode.Replace(" ", "").ToUpper();
        postcode = postcode.Insert(postcode.Length - 3, " ");
        var postcodeMatches = Enumerable.Range(0, postcode.Length)
                                    .Select(x => postcode.Substring(0, x + 1))
                                    .ToArray();

        return x => x.CustomerID == customerId && postcodeMatches.Contains(x.Postcode);
    }
}

這是單元測試覆蓋率(用 xUnit 編寫):

public static int validCustomerId = 1;
public const string validPostcode = "SW1A0AA";

public static IEnumerable<object[]> CheckPostcodeExclusion_should_validate_postcode_Inputs = new List<object[]>
{
    new object[] { true, validPostcode, validCustomerId, new List<PostcodeExclusion> { new PostcodeExclusion() { CustomerID = validCustomerId, Postcode = "A" } } },
    new object[] { true, validPostcode, validCustomerId, new List<PostcodeExclusion> { new PostcodeExclusion() { CustomerID = validCustomerId, Postcode = "SX" } } },
    //other test cases...    
};

[Theory]
[MemberData(nameof(CheckPostcodeExclusion_should_validate_postcode_Inputs))]
public async Task CheckPostcodeExclusion_should_validate_postcode(bool expectedPostcodeIsSupported, string postcode, int customerId, IEnumerable<PostcodeExclusion> postcodeExclusionSet)
{
    var isSupported = await postcodeExclusionSet.AsQueryable().CheckPostcodeIsSupportedAsync(customerId, postcode);
    Assert.Equal(expectedPostcodeIsSupported, isSupported);
}

當我針對Async方法運行測試時,我得到

源 IQueryable 的提供程序未實現 IAsyncQueryProvider。 只有實現 IAsyncQueryProvider 的提供程序才能用於實體框架異步操作。

我發現這個解決方法但是它只適用於 EF 核心 2.2。 我試圖以某種方式為 EF 核心 3.1 實現類似的想法,但沒有結果。 目前我用測試覆蓋了非異步方法,但是我在生產中使用了異步方法。 有東西總比沒有好...有什么想法嗎? 干杯

如果您創建一個名為AnyAsyncOrSync()的擴展方法,它會在嘗試之前檢查源提供程序是否能夠進行異步枚舉......

public static async Task<bool> AnyAsyncOrSync<TSource>([NotNull] this IQueryable<TSource> source, [NotNull] Expression<Func<TSource, bool>> predicate, CancellationToken cancellationToken = default)
{
    return source is IAsyncEnumerable<TSource>
        ? await source.AnyAsync(predicate, cancellationToken)
        : source.Any(predicate);
}

...您可以在問題進入框架之前發現問題...

!await postcodeExclusion.AnyAsyncOrSync(ApplyIsSupportedExpression(customerId, postcode));

為什么不使用 .Result 強制方法調用同步,如下所示: http : //bulletproofcoder.com/blog/testing-async-methods-with-xunit

編輯:由於上述鏈接可能並不總是可用。 單元測試異步方法的兩種方法,通常可以通過使用來實現.ResultGetAwaiter().GetResult()針對異步方法。 經過進一步研究,似乎 xUnit 應該能夠通過在測試方法中返回async Task來處理調用異步操作。 請參閱此處: https : //makolyte.com/csharp-how-to-unit-test-async-methods/

例子:

[TestMethod] public async Task SumTest_WhenInput1And2_Returns3()

返回IQueryable異步是沒有意義的。 它不返回數據,只是一個查詢存根。

返回IEnumerableAsync或將其包裝在Task.Run( () => YourMethod())

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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