【问题标题】:Best practices to have a test base class - .NET拥有测试基类的最佳实践 - .NET
【发布时间】:2021-11-06 21:01:43
【问题描述】:

我开始为应用编写测试,例如:

namespace API.Test
{
    public class HealthCheckTests : IClassFixture<WebApplicationFactory<Startup>>
    {
        private readonly HttpClient _httpClient;

        public HealthCheckTests(WebApplicationFactory<Startup> factory)
        {
            _httpClient = factory.CreateDefaultClient(new Uri("http://localhost/healthcheck"));
        }

        [Fact]
        public async Task HealthCheck_ReturnsOk()
        {
            var response = await _httpClient.GetAsync("");

            response.EnsureSuccessStatusCode();
        }
    }
}

当然,我有更多的控制器,所以我想“重用”客户端。您认为哪种方法更好。

  • 抽象类初始化构造函数
  • 在构造函数中进行类和实例化,如组合。
  • 其他

【问题讨论】:

    标签: .net testing xunit


    【解决方案1】:

    其他。您实际执行的操作取决于您测试的内容、您想要配置的内容以及您的个人偏好。在这种情况下,虽然唯一可以抽象的是对CreateClientCreateDefaultClient 的调用,这并不多。也可能不需要缓存该实例。

    许多测试会创建一个单独的 Application 类,该类继承自 WebApplicationFactory&lt;&gt;,允许您覆盖配置或添加可能有用的额外成员。

    In these tests 例如,PlaygroundApplication 类用于设置环境并将配置的 DbContext 替换为使用内存中 SQLite 的 DbContext。该 repo 演示了 .NET 6 最小 API,因此它使用 Program 而不是 Startup,但这根本不会影响测试:

    internal class PlaygroundApplication : WebApplicationFactory<Program>
    {
        private readonly string _environment;
    
        public PlaygroundApplication(string environment = "Development")
        {
            _environment = environment;
        }
    
        protected override IHost CreateHost(IHostBuilder builder)
        {
            builder.UseEnvironment(_environment);
    
            // Add mock/test services to the builder here
            builder.ConfigureServices(services =>
            {
                services.AddScoped(sp =>
                {
                    // Replace SQLite with in-memory database for tests
                    return new DbContextOptionsBuilder<TodoDb>()
                        .UseInMemoryDatabase("Tests")
                        .UseApplicationServiceProvider(sp)
                        .Options;
                });
            });
    
            return base.CreateHost(builder);
        }
    }
    

    这个类是used in tests directly。它甚至不被用作类夹具:

    [Fact]
    public async Task GET_Root_Responds_OK()
    {
        await using var application = new PlaygroundApplication();
    
        using var client = application.CreateClient();
        using var response = await client.GetAsync("/");
    
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
        Assert.Equal("Hello World!", await response.Content.ReadAsStringAsync());
    }
    

    在那之后,这是一个风格和方便的问题。您可以:

    • 具有单独配置的多个此类类。如果配置复杂和/或重复,例如针对不同的场景,并且您不想在配置中乱扔测试,则很有用。
    • 通过构造函数选项参数化的单个。如果选项很少,则很有用。如果选项太多,则可能是时候将其拆分为单独的类了。配置一个超灵活的类可能比创建几个不同的特殊类型更麻烦
    • TestApplication 基类,具有通用配置和适用于多种场景的专用派生类。

    所有选项都可以用作固定装置。您可以将这些选项中的任何一个与Theory 结合使用,以使用不同的配置运行相同的测试。

    【讨论】:

      猜你喜欢
      • 2010-11-17
      • 2011-06-29
      • 2020-02-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-09
      • 1970-01-01
      相关资源
      最近更新 更多