asp-net core 集成测试每次测试后回滚事务

asp-net core integration tests rollback transaction after each tests

我已将此文档用于我的集成测试:asp.net core integration tests 这是我的简化集成测试:

 public class ApplicationControllerTests : IClassFixture<CustomWebApplicationFactory<Startup>>
 {
     private readonly CustomWebApplicationFactory<Startup> _factory;

     public ApplicationControllerTests(CustomWebApplicationFactory<Startup> factory)
     {
           _factory = factory;
     }

    [Fact]
    public async Task AcceptOffer_WhenCalled_ShouldReturnSuccess()
    {
        var httpClient = factory.CreateClient();

        var acceptOfferRequest = new AcceptOfferRequest
        {
            OfferId = 1,
            OfferType = 1
        }.ToJsonStringContent();

        var response = await httpClient.PostAsync("/api/v1/Application/AcceptOffer", acceptOfferRequest);

        response.EnsureSuccess();

        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
    }
}

如您所见,我正在向“/api/v1/Application/AcceptOffer”API 发送一个 HTTP 请求,它会更新数据库中的一些实体,如果一切正常,returns 状态代码为 200。

测试后,我想像测试前一样清理数据库。为此,我想将我的测试放在事务中,并在每次测试完成后回滚。目前的实施是否可能?删除和重新创建数据库的成本很高,这就是为什么我想将我的测试放在事务中。

我正在使用带有 entity framework ORM 的 MS SQL 数据库。

一个解决方案是使用 DbContext 的单例,然后将所有测试包装在事务中,但问题是 SQL 不支持嵌套事务,如果 API 也使用事务,它将抛出运行时例外。 另一种解决方案是使用 C# 的 TransactionScope,但它不适用于 SQL 事务并且不适用于不同的线程。

好的,我发现没有很好的方法或可能使用事务来解决这个问题。我编写了全局脚本,而不是事务,我在其中手动截断所有表并在每次测试后执行它。这显然比删除并重新创建数据库要快。

我认为在不对应用程序本身进行任何调整的情况下,不可能将数据库事务从测试传递到 Web 应用程序。除非 TestServer 在同一进程中托管应用程序在这里可能会有一些帮助。

一个可能的想法(虽然不确定我是否喜欢)将涉及以下应用程序更改:

  • 在您的应用中引入特殊的仅测试端点,它将接受一个标识符(例如 guid),启动一个事务并在应用状态中按标识符保存该事务;
  • 将此事务标识符附加到测试中执行的每个请求并编写一个中间件,它将从应用程序状态中检索现有事务并将其附加到请求的 DbContext;
  • 引入另一个端点以通过其 id 恢复交易;
  • 确保所有这些逻辑仅适用于测试环境。

如果您决定尝试一下,我建议您检查一下 TestServer 是如何工作的,以及它是否可以以某种方式用于实现相同的想法,而无需为事务引入专用端点 creation/rollback。

你是对的,不可能使用这种方法来测试逻辑,逻辑本身需要事务,因为事务通常不能嵌套。因此优化将仅适用于您的测试部分,其余部分将需要以不同方式执行清理。

但如果我是你,在这种情况下我宁愿使用其他数据库恢复方式而不是事务。诸如数据库快照或 insert/delete 脚本之类的东西——你实际走的路线。

话虽如此,我建议您查看 Reseed library I'm currently developing, which is able to take care of db restoring by generating insert and delete scripts and executing them fast. There is also Respawn 数据库清理库,它也适合您的情况。


更新

根据此 和回复,使用 TransactionScope 类型可以实现非常接近所需的行为。它与 TestServer 一起按预期工作,但它有缺点,正如主题发起者已经提到的那样。