我从事的项目没有集成测试设置。
当我处理一些后端任务时,我需要对与数据库交互相关的内容进行一些测试。
我使用 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 的提供程序才能用于实体框架异步操作。
我发现 this workaround 但它仅适用于 EF 核心 2.2。我试图以某种方式为 EF 核心 3.1 实现类似的想法,但没有结果。目前我用测试覆盖了非异步方法,但是我在生产中使用了异步方法。有总比没有好... 有任何想法吗?干杯
答案 0 :(得分: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));
答案 1 :(得分:-1)
为什么不直接使用 .Result 强制方法调用同步,如下所示:http://bulletproofcoder.com/blog/testing-async-methods-with-xunit?
编辑:由于上述链接可能并不总是可用。通常可以通过对异步方法使用 .Result
或 GetAwaiter().GetResult()
来实现对异步方法进行单元测试的两种方式。经过进一步研究,似乎 xUnit 应该能够通过在测试方法中返回 async Task
来处理调用异步操作。看这里:
https://makolyte.com/csharp-how-to-unit-test-async-methods/
示例:
[TestMethod]
public async Task SumTest_WhenInput1And2_Returns3()
答案 2 :(得分:-1)
异步返回 IQueryable
没有意义。它不返回数据,只是一个查询存根。
返回 IEnumerableAsync
或将其包裹在 Task.Run( () => YourMethod())
中