避免 ASP NET Core 集成测试之间共享内存

问题描述 投票:0回答:1

我正在使用 NUnit 为 ASP NET Core 应用程序编写集成测试。我在测试中观察到一个奇怪的行为,似乎所有测试都在我的 SUT 中共享相同的静态属性。

我正在使用 WebApplicationFactory 来引导我的应用程序并与其交互。我的假设是,我在每个 WebApplicationFactory 中使用应用程序的“新鲜”实例,并且每个测试使用一个工厂......

我创建了一个最小的可重现示例,其中只有一个在启动时递增的静态 int 字段。端点返回字段的值。 我的测试重复了多次,预计总是返回 1。但正如您所料,它在第二次运行期间失败了。

程序.cs

internal class Program
{
    private static int a = 0;
    private static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);
        var app = builder.Build();

        a++;
        app.MapGet("/test", () => a);
        app.Run();
    }
}

单元测试

using Microsoft.AspNetCore.Mvc.Testing;

namespace Tests
{
    public class Tests
    {
        [Test]
        [Repeat(3)]
        public async Task Test1()
        {
            var factory = new WebApplicationFactory<Program>();
            var client = factory.CreateClient();
            var res = await client.GetAsync("/test");
            var number = int.Parse(await res.Content.ReadAsStringAsync());
            Assert.That(number, Is.EqualTo(1));
        }
    }
}

我确信这与WebApplicationFactory如何启动程序有关,但这仍然是非常意外的,并且当我阅读docs时没有提及它。 也许这与我使用 NUnit 而不是文档中的 xUnit 有关?但是 NUnit 的行为是如何影响这一点的呢?

我还尝试使用 NUnits [NonParallelized] 来确保测试按顺序运行(无论如何这都是必需的),并且在我的非示例项目中,我每个测试使用一个类。

我发现了这种行为,因为我在启动时使用 BsonClassMap 注册 ClassMap,并且在测试中遇到了有关重复键的错误。

我主要是在寻找如何以某种方式运行我的测试的答案,即它们不共享状态。我也很好奇为什么会发生这种行为以及是否有意为之。

.net asp.net-core testing nunit integration-testing
1个回答
0
投票

WebApplicationFactory
对于最小托管基本上为每个实例工厂实例(这是您的Assembly.EntryPoint
方法)运行
Main
,因此您将为工厂的每个创建和“启动”实例调用
a++;
(它包括诸如
CreateClient
Services
Server
之类的调用),并且由于
a
是一个静态字段,因此它将在整个应用程序生命周期中共享(基本上根据定义)。

至少有以下几种选择:

  1. 从使用静态转向单例服务。 IE。将

    a
    封装在某些服务中,并将其注册为单例并在适当的情况下进行解析。可以说,在一般情况下这是更好的方法。

  2. 对所有测试使用单个

    WebApplicationFactory
    实例(因此仅执行一次设置)。例如,将
    OneTimeSetUp
    与根夹具结合使用。例如,将如下内容添加到测试项目的根目录中:

    public class GlobalFixture
    {
        public static WebApplicationFactory<Program> ApplicationFactory;
    
        [OneTimeSetUp]
        public void Setup() => ApplicationFactory = new WebApplicationFactory<Program>();
    
        [TearDown] 
        public void TearDown() => ApplicationFactory.Dispose();
    }
    

    并在需要时使用

    GlobalFixture.ApplicationFactory

我的假设是我正在为每个 WebApplicationFactory 使用应用程序的“新鲜”实例

它使用的是一个新的实例(我想说实际上是通过第二次运行时失败的测试证明的),但您仍然在整个过程中共享相同的静态字段。

也许这与我使用 NUnit 而不是文档中的 xUnit 有关?但是 NUnit 的行为是如何影响这一点的呢?

如您所见,它本身并不是 NUnit。

© www.soinside.com 2019 - 2024. All rights reserved.