我在使用dotMemory、xUnit和async时有什么做错的地方吗?

6
我有一个单元测试,要验证我是否已经处理了曾经附加到主用户界面的文档。该单元测试必须是异步的,因为一切都需要在STA线程下运行,并且我必须等待用户界面被创建。
我有一个助手,可以将操作分派到STA线程上。
我在测试的主体中创建了memory对象,然后将其传递给下面的异步方法。
请查看用###标注的代码行,以查看实际的问题行。dotMemory报告对象尚不存在,但我已经做出了一个断言,证明对象确实存在。
(可以在 https://gist.github.com/bradphelan/cb4f484fbf6a7f9829de0dd52036fd63 找到STA助手类)
这是与异步和dotMemory相关的问题吗?
    [Collection("Memory leaks")]
public class MemLeakSpec
{
    public MemLeakSpec(ITestOutputHelper output)
    {
        DotMemoryUnitTestOutput.SetOutputMethod(output.WriteLine);
    }

    [Fact]
    [DotMemoryUnit(FailIfRunWithoutSupport = true)]
    public void ShouldCollectProjectX()
    {
        dotMemory.Check (memory => { STAThread.Run(() => ShouldCollectProjectAsyncX(memory)).Wait(); });
    }

    class Document { }

    class Container { public Document Document; };

    Document CreateFoo() => new Document();

    private async Task ShouldCollectProjectAsyncX(Memory memory)
    {
        await Task.Delay(TimeSpan.FromMilliseconds(50));

        Container container = new Container();

        memory.GetObjects(@where => @where.Type.Is<Document>())
                         .ObjectsCount.Should()
                         .Be(0);

        Document documentA = CreateFoo();
        container.Document = documentA;

        // Verify with dotMemory that the object exists.
        // ### This fails even though I have verified
        // ### the document exists
        memory.GetObjects(@where => @where.Type.Is<Document>())
                         .ObjectsCount.Should()
                         .Be(1);

        // Open a new project which should dispose the old one and thus
        // remove any reference to GenericWeinCadFolder
        container.Document = null;

        memory.GetObjects(@where => @where.Type.Is<Document>())
                         .ObjectsCount.Should()
                         .Be(0);


        GC.KeepAlive(container);
    }
}

我已经创建了一个同步版本的上述测试,它不会失败。下面有两个测试ShouldCollectAsyncShouldCollectSync异步的一个失败了,而同步的一个通过了。

[Collection("Memory leaks")]
public class MemLeakSpec
{
    public MemLeakSpec(ITestOutputHelper output)
    {
        DotMemoryUnitTestOutput.SetOutputMethod(output.WriteLine);
    }

    [Fact]
    [DotMemoryUnit(FailIfRunWithoutSupport = true)]
    public void ShouldCollectAsync()
    {
        dotMemory.Check (memory => { STAThread.Run(() => ShouldCollectProjectAsyncX(memory)).Wait(); });
    }

    /// This test is almost identical to the ShouldCollectAsync
    /// but it passes
    [Fact]
    [DotMemoryUnit(FailIfRunWithoutSupport = true)]
    public void ShouldCollectSync ()
    {
        dotMemory.Check (memory => { STAThread.Run(() => ShouldCollectProjectSync(memory)); });
    }

    class Document { }

    class Container { public Document Document; };

    Document CreateFoo() => new Document();

    private async Task ShouldCollectProjectSync(Memory memory)
    {
        Container container = new Container();

        memory.GetObjects(@where => @where.Type.Is<Document>())
                         .ObjectsCount.Should()
                         .Be(0);

        Document documentA = CreateFoo();
        container.Document = documentA;

        // Verify with dotMemory that the object exists.
        // #### Passes here
        memory.GetObjects(@where => @where.Type.Is<Document>())
                         .ObjectsCount.Should()
                         .Be(1);

        GC.KeepAlive(documentA);
        GC.KeepAlive(container);
    }
    private async Task ShouldCollectProjectAsyncX(Memory memory)
    {

        await Task.Delay(TimeSpan.FromMilliseconds(50));

        Container container = new Container();

        memory.GetObjects(@where => @where.Type.Is<Document>())
                         .ObjectsCount.Should()
                         .Be(0);

        Document documentA = CreateFoo();
        container.Document = documentA;

        // Verify with dotMemory that the object exists.
        // #### FAILS here
        memory.GetObjects(@where => @where.Type.Is<Document>())
                         .ObjectsCount.Should()
                         .Be(1);

        GC.KeepAlive(documentA);
        GC.KeepAlive(container);
    }
}

我尝试将 dotMemory.Check 调用放在异步方法中,但是 dotMemory 告诉我我正在尝试运行并行测试。 - bradgonesurfing
获取对象是否异步运行? - marshal craft
不,它不会。 - bradgonesurfing
也许我错了,但当你声明方法为异步时,方法内部的操作会异步运行。因此,当你将其分配给文档时,在下一个操作中检查它是否为零之后,它还没有完成。关键字“await”将强制等待直到完成。所以像这样使用:“await document adocument ...” - marshal craft
你错了。async 不会使方法中的 all 行异步运行。我建议你在发表评论之前实际编写一些 async 代码 :)。如果 CreateFoo 被声明为 Task<Document> CreateFoo() 而不是 Document CreateFoo(),那么你就有点道理了,但是那样的话代码将无法编译。 - bradgonesurfing
显示剩余3条评论
2个回答

2

对我而言,只需要在本地函数中包装测试即可:

public void TestMethod()
{
    async Task LocalFunction()
    {
        // here you can make async calls
        await Task.Delay(...);
    }

    LocalFunction().ConfigureAwait(true).GetAwaiter().GetResult();
}

1

dotMemory Unit要求所有方法都必须从“test”方法中调用。 可以将其视为在ShouldCollectAsync的最开始处调用dotMemoryUnit.TestStart,然后在完成时调用dotMemoryUnit.TestEnd。 由于您没有公开STAThread.Run的实现,因此我无法给出更详细的建议,但是想法是在测试方法中等待异步例程完成。


我刚刚也得出了同样的结论。 - bradgonesurfing

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接