【问题标题】:xUnit Theory with async MemberData带有异步 MemberData 的 xUnit 理论
【发布时间】:2018-10-14 15:41:39
【问题描述】:

我有一个使用 xUnit.net v.2.3.1 的单元测试项目,用于我的 ASP.NET Core 2.0 Web 应用程序。

我的测试应该专注于测试给定的 DataEntry 实例:DataEntry 实例由我的 DataService 类中的异步方法 GenerateData() 生成,如下所示:

public class DataService {
    ...
    public async Task<List<DataEntry>> GenerateData() {
        ...
    }
    ...
}

我将这个测试用例写成一个理论,这样我的测试就可以一次集中在一个 DataEntry 实例上。代码如下:

[Theory]
[MemberData(nameof(GetDataEntries))]
public void Test_DataEntry(DataEntry entry) {

    // my assertions
    Assert.NotNull(entry);
    ...

}

public static async Task<IEnumerable<object[]>> GetDataEntries() {

    var service = new DataService();
    List<DataEntry> entries = await service.GenerateData().ConfigureAwait(false);

    return entries.Select(e => new object[] { e });

}

但是,我在编译时收到以下错误:

MemberData 必须引用可分配给“System.Collections.Generic.IEnumerable”的数据类型。引用的类型 'System.Threading.Tasks.Task>' 无效。

从错误描述来看,xUnit.net 似乎不允许 MemberData 使用异步静态方法,例如我的 GetDataEntries() 方法。 xUnit.net 中是否存在我应该注意的功能差距?

现在,我知道我可以将我的 Theory 转换为 Fact 并遍历 DataService 返回的列表中的每个 DataEntry,但是我更愿意保留 Theory 设置,因为我的测试会更简洁,并且专注于 DataEntry 而不是 List .

问题xUnit.net 有什么方法可以让我的 Theory 从我的 DataService 异步 API 获取数据?请注意,不能更改或扩展 DataService 类以同步提供数据。

编辑

我正在寻找一种通过 async/await 的方法,并且希望避免使用任何阻塞调用,例如 Task.Result,例如在我的 GenerateData() 方法上,因为底层线程将被阻塞,直到操作完成。 这在我的测试项目中是相关的,因为我有其他类似的测试用例应该以相同的方式检索数据,因此我希望避免以过多的阻塞调用结束,而是保持 async/await 传播。

【问题讨论】:

  • 我不知道结果如何,但请在您的GenerateData 上尝试.Result
  • 感谢您的输入,但我实际上更愿意通过 async/await 找到一种方法,因为通过使用 Task.Result,底层线程将被阻塞,直到操作完成。一般来说,如果这是一个孤立的场景,这会很好,但我实际上还有其他几个类似的测试用例,我需要从我发布的异步调用中检索数据,因此我想避免结束太多阻塞来电。
  • @PauloMorgado 从您的评论中我刚刚意识到我的问题不够精确,因此我只是编辑了。
  • 你试过了吗?
  • xUnit 存储库中的问题参考:github.com/xunit/xunit/issues/1698

标签: c# asp.net-core async-await xunit.net


【解决方案1】:

在 xUnit 允许异步理论数据之前,您可以使用 Task&lt;T&gt; 实例作为理论数据并在测试方法中等待它们(注意,测试方法可以是异步的):

public static IEnumerable<object> GetDataEntries() {
    var service = new DataService();
    yield return new object[] { service.GenerateData() };
}

[Theory]
[MemberData(nameof(GetDataEntries))]
public async Task Test_DataEntry(Task<List<DataEntry>> task) {
    List<DataEntry> entries = await task;

    for (int i = 0; i < entries.Count; i++) {
        // my assertions
        Assert.NotNull(entries[i]);
    }
}

【讨论】:

    【解决方案2】:

    内部不提供此功能。您可以尝试以下操作:

    1. 通过继承DataAttribute来编写你的CustomMemberDataAttribute
    2. 覆盖父类的“GetData”方法。
    3. 使方法异步,即提供数据。
    4. 从“GetData”方法调用异步数据提供程序方法。
    5. 使用您的 CustomMemberDataAttribute 来装饰测试用例。

    您可以参考以下链接来编写您的自定义属性。 保持其他方法相同,只需修改上面讨论的“GetData”方法。 https://github.com/xunit/xunit/blob/bccfcccf26b2c63c90573fe1a17e6572882ef39c/src/xunit.core/MemberDataAttributeBase.cs

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-11-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-26
      • 1970-01-01
      • 2017-07-31
      相关资源
      最近更新 更多