无法在 xUnit 中捕获 InvalidOperationException

Unable to catch InvalidOperationException in xUnit

过去一个小时我一直在尝试通过以下测试,但我似乎无法让它工作:

[Fact]
public async void TestDetachedRecordsArentUpdatedWithoutIDs()
{
    var options = new DbContextOptionsBuilder<ClientContext>()
        .UseInMemoryDatabase(databaseName: "cant_update_detached_clients_with_bankinfos")
        .Options;

    int clientID, bankInfoID;
    using(var context = new ClientContext(options))
    {
        var service = new ClientService(context);
        var bankInfo = new BankInfo { RoutingNumber = "12345" };
        var client = new Client { FirstName = "Javier", LastName = "Garcia", BankInfo = bankInfo };
        await service.Save(client);
        clientID = client.ID;
        bankInfoID = client.BankInfo.ID;
    }

    using(var context = new ClientContext(options))
    {
        var service = new ClientService(context);
        var bankInfo = new BankInfo { RoutingNumber = "Modified" };
        var client = new Client { ID = clientID, FirstName = "Modified", BankInfo = bankInfo };

        try
        {
            await service.Save(client);
        }
        catch (System.InvalidOperationException ex)
        {
            var expected = "The property 'ID' on entity type 'BankInfo' is part of a key and so cannot be modified or marked as modified.";
            Assert.Contains(expected, ex.Message);
        }
    }
}

我发现的问题是我似乎无法捕捉到异常。这是我每次测试的结果运行吧:

为了提供一点额外的上下文,当调用 ClientService::Save 时,它会调用一个名为 HandleDisconnectedEntities 的方法,当下面的行 运行s:

94:  _context.Entry(existingClient.BankInfo).CurrentValues.SetValues(client.BankInfo);

我确实了解异常的性质,但我不明白为什么我的测试无法捕获它。非常感谢任何见解!

这个问题是因为测试定义的 async void

public async void TestDetachedRecordsArentUpdatedWithoutIDs() { ...

Async void methods have different error-handling semantics. When an exception is thrown out of an async Task or async Task<T> method, that exception is captured and placed on the Task object. With async void methods, there is no Task object, so any exceptions thrown out of an async void method will be raised directly on the SynchronizationContext that was active when the async void method started.

Exceptions from an Async Void Method Can’t Be Caught with Catch

例如

private async void ThrowExceptionAsync()
{
  throw new InvalidOperationException();
}

public void AsyncVoidExceptions_CannotBeCaughtByCatch()
{
  try
  {
    ThrowExceptionAsync();
  }
  catch (Exception)
  {
    // The exception is never caught here!
    throw;
  }
}

将测试改为使用 Task

public async Task TestDetachedRecordsArentUpdatedWithoutIDs() {

    //...

}

引用Async/Await - Best Practices in Asynchronous Programming