C# 当使用匿名异步方法调用方法时,xUnit测试挂起/死锁

C# 当使用匿名异步方法调用方法时,xUnit测试挂起/死锁,c#,xamarin,async-await,xunit,xunit.net,C#,Xamarin,Async Await,Xunit,Xunit.net,我有一个xUnit(2.1.0)测试,它总是挂起/死锁。此处,为了清晰和保密,更改了类/方法名称的代码: [Fact] public void DocumentModificationTest() { Exception ex = null; using (TestDependency testDependency = new TestDependency()) { TestDependencyDocument testDependencyDoc = te

我有一个xUnit(2.1.0)测试,它总是挂起/死锁。此处,为了清晰和保密,更改了类/方法名称的代码:

[Fact]
public void DocumentModificationTest()
{
    Exception ex = null;
    using (TestDependency testDependency = new TestDependency())
    {
        TestDependencyDocument testDependencyDoc = testDependency.Document;
        MockTestDependency fakeDependency = new MockTestDependency();
        try
        {
            DoStuffToTheDocument(testDependencyDoc, "fileName.doc", fakeDependency);
        }
        catch (Exception e)
        {
            ex = e;
        }
    }
    Assert.Null(ex);
}
如果我设置了一个断点并跳过直到断言,我可以看到ex为null,测试应该通过并完成,但它只是挂起,我从来没有在运行程序上看到测试成功

以下是Dostuff TotheDocument的外观:

public static void DoStuffToTheDocument(TestDependencyDocument document, string pFileName, MockTestDependency pContainer)
{
    pContainer.CheckInTheDocFirst(async () =>
    {
        //check some stuff
        //test returns early here

        //check other stuff(test never gets here)
        //await method (thus the async anonymous method)
    });
}
public void CheckInTheDocFirst(Action pConfirmAction) 
{
    pConfirmAction(); //since this is a method in a mock class only used for testing we just call the action
}
最后这里是CheckInDocFirst的外观:

public static void DoStuffToTheDocument(TestDependencyDocument document, string pFileName, MockTestDependency pContainer)
{
    pContainer.CheckInTheDocFirst(async () =>
    {
        //check some stuff
        //test returns early here

        //check other stuff(test never gets here)
        //await method (thus the async anonymous method)
    });
}
public void CheckInTheDocFirst(Action pConfirmAction) 
{
    pConfirmAction(); //since this is a method in a mock class only used for testing we just call the action
}

你知道这里发生了什么吗?我的async Wait范例中是否有什么东西导致此测试挂起

当你有一个异步函数时,你应该一直都是异步的。否则,您会遇到同步上下文被阻止的问题,这将导致锁定

pContainer.checkinDocFirst应该是异步的并返回一个任务(因为它使用一个异步函数返回一个任务对象)

DoStuffToDocument应该是返回任务的异步函数,因为它调用异步函数

最后,测试本身也应该是一个返回任务的异步方法


如果您一直运行async,我想您会发现一切正常。

事实证明,这是由xUnit中的async void测试方法支持引起的问题:

虽然您确实应该一直使用异步,但有时由于互操作性问题,这是不可行的。你不能总是更改你正在处理的每件事的签名

但是,关于断言,需要注意的一点是,即使异步void方法(在本例中是lambda)中抛出异常,它也将始终被证明是正确的。这是因为:

异步void方法具有不同的错误处理语义。当从异步任务或异步任务方法抛出异常时,将捕获该异常并将其放置在任务对象上。对于异步void方法,没有任务对象,因此从异步void方法抛出的任何异常都将直接在异步void方法启动时处于活动状态的SynchronizationContext上引发。图2说明了异步void方法抛出的异常不能自然捕获


async
lambda传递给
Action
参数将创建
async void
方法,该方法众所周知很难测试,但不应导致死锁。你在什么地方堵车吗?(请给出一个最小但完整的示例)。@StephenCleary我不认为有任何东西会阻塞。事实上,我在测试中得到了断言,这让我相信没有任何东西是阻塞的。我遗漏的唯一代码(被测试击中的代码)是对匿名异步函数中Path类的几个调用。下面有一个更复杂的代码,但它从来没有被击中,因为我很早就返回了,我错过了它确实到达了断言;那就不堵了。你的异步lambda完成了吗?