C# 如何编写测试以公开从使用块返回的任务?

C# 如何编写测试以公开从使用块返回的任务?,c#,unit-testing,async-await,C#,Unit Testing,Async Await,我编写了以下错误异步方法,该方法通过了单元测试,但在生产中失败(EDIT:在生产中,它抛出了一个ObjectDisposedException): 我的问题是:我如何编写一个单元测试,该测试在错误版本的代码中可靠地失败? 编辑:以下是当前(不足的)单元测试,它没有暴露此问题: [Test] public async Task GivenFileUtilsWhenReadAllTextThenGetsText() { var file

我编写了以下错误异步方法,该方法通过了单元测试,但在生产中失败(EDIT:在生产中,它抛出了一个
ObjectDisposedException
):

我的问题是:我如何编写一个单元测试,该测试在错误版本的代码中可靠地失败?

编辑:以下是当前(不足的)单元测试,它没有暴露此问题:

    [Test]
    public async Task GivenFileUtilsWhenReadAllTextThenGetsText()
    {            
        var fileUtils = new FileUtils(); // the prod code above is in class FileUtils
        var path = @"C:\tmp\foo.txt";
        var expected = "foo";
        File.WriteAllText(path, expected);

        var text = await fileUtils.ReadAllText(path);

        text.Should().Be(expected);
    }

问题是,您没有在文件中放入足够的内容,从而导致在异步读取时释放该文件

因此,这并不是说您的代码是同步执行的。它完成任务的速度太快,导致生产中出现问题

为了证明这一点,我只是添加了更多的内容

for (int i = 0; i < 10; i++) { expected += expected; } 
for(inti=0;i<10;i++){expected+=expected;}
在写入文件并运行测试之前

var fileUtils = new FileUtils(); // the prod code above is in class FileUtils
var path = @"foo.txt";
var expected = "foo";
for (int i = 0; i < 15; i++) {
    expected += expected;
}
File.WriteAllText(path, expected);

var text = await fileUtils.ReadAllText(path);

text.Should().Be(expected);
var fileUtils=new fileUtils();//上面的产品代码位于FileUtils类中
var path=@“foo.txt”;
var expected=“foo”;
对于(int i=0;i<15;i++){
预期+=预期;
}
File.WriteAllText(路径,应为);
var text=await fileUtils.ReadAllText(路径);
text.Should().Be(应为);
确实,对象处理异常导致测试失败

通过使用块在
中等待,您已经找到了问题代码的解决方案

编写测试来发现您所寻找的内容将是相当困难的,但是代码分析器在编译时发现此类错误的机会会更大

我该如何编写一个单元测试,它会因为错误版本的代码而可靠地失败

根据需求来构建它。这是您要测试的内容:

“SUT不应立即处理流。它可以在流被完全读取后处理流。”

为此,您的单元测试需要控制:

  • 正在处理的流,因此它可以检测何时发生处理
  • 当流被完全读取时
  • 这两个都可以使用自定义流类型来处理

    对于第一个存根需求,当调用
    Dispose
    时,自定义流类型可以将
    bool Disposed
    属性设置为
    true


    对于第二个存根需求,您的自定义流类型可以实现为仅在接收到信号后完成异步操作。“异步信号”的一种类型是
    TaskCompletionSource
    ——您可以在自定义流中创建实例,让每个
    async
    方法
    wait
    Task
    属性,当您的单元测试准备好让流完成时,它可以很好地完成
    TaskCompletionSource
    ,不一定,尽管那很好。我只是想让单元测试失败。这个问题更新得很好。正在审阅。Ok运行了一些测试。你的代码很好。问题是,您没有在文件中放入足够的内容以使其被释放。它完成得太快了。在写入文件并运行测试之前,我刚刚为(int i=0;i<10;i++){expected+=expected;}
    添加了更多内容
    ,并确保有足够的对象处理异常。这是一个选项吗?将整个方法替换为
    System.IO.File.ReadAllTextAsync(path)
    。现在它不是你的代码,你也不必为它编写测试。那么就让内容变长吧?但它仍然不能保证失败,对吗?这种方法总是依赖于系统的,看起来这个测试确实证明了原始代码在失败的测试中是有缺陷的。这将需要重构SUT才能操作流。考虑到当前代码的紧密耦合,这是我看到它被完成的唯一方式。SUT是什么?SUT=测试中的系统。在本例中,
    FileUtils.ReadAllText
    for (int i = 0; i < 10; i++) { expected += expected; } 
    
    var fileUtils = new FileUtils(); // the prod code above is in class FileUtils
    var path = @"foo.txt";
    var expected = "foo";
    for (int i = 0; i < 15; i++) {
        expected += expected;
    }
    File.WriteAllText(path, expected);
    
    var text = await fileUtils.ReadAllText(path);
    
    text.Should().Be(expected);