如何使用WebApplicationFactory对.NET 6 Web Api项目进行集成测试?

4

我一直在.NET 5、.NET Core 3.1和.NET Core 2.1中使用WebApplicationFactory进行集成测试,一切都很正常。

我只需使用以下命令创建我的项目:

dotnet new webapi

然后我看到一个带有启动类的 Web API 项目

接着我创建了一个工厂类的实例:

private readonly WebApplicationFactory<Startup> factory = new WebApplicationFactory<Startup>();

这里这里所解释的。

WebApplicationFactory类是我非常喜欢的东西。它使编写集成测试变得轻松。我非常喜欢它。Microsoft文档也将其用于.NET 6

然而,在使用.NET 6时,我如何确保我的代码编译通过?

上述命令行语句:

dotnet new webapi

导致项目能正常工作但却没有Startup类(安装了.NET 6 SDK)......现在我的测试由于一个原因无法编译:Startup类不存在。

如何解决?我很难想象,因为WebApplicationFactory<Startup>不再编译,就不能再写集成测试了。但现在这是我需要处理的现实。我该如何解决这个问题?我期待着能够使用WebApplicationFactory类并继续编写集成测试。但是怎么做呢?

1个回答

10

支持该功能的新增于 .NET 6 RC1版本。这是第一个可在生产环境中使用的版本。

您可以将 Program 传递给 Startup。现在,Startup 已经合并到了 Program 中。 Program 类是从 Program.cs 文件中的顶级语句自动生成的,但它不是公共类,因此需要在应用程序的项目文件中添加 InternalsVisibleTo,以使其对测试项目可见。

  <ItemGroup>
    <InternalsVisibleTo Include="MinimalApiPlayground.Tests" />
  </ItemGroup>

这个例子中,Damien Edwards定义了一个WebApplicationFactory<Program>来演示如何测试Todo minimal API。
创建测试应用程序的过程实际上与使用Startup相同:
[Fact]
public async Task GetSwaggerUI_Returns_OK()
{
    await using var application = new TodoApplication();

    var client = application.CreateClient();
    var response = await client.GetAsync("/swagger/index.html");

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

class TodoApplication : WebApplicationFactory<Program>
{
    protected override IHost CreateHost(IHostBuilder builder)
    {
        // Add mock/test services to the builder here
        builder.ConfigureServices(services =>
        {
            services.AddScoped(sp =>
            {
                // Replace SQL Lite with test DB
                return new SqliteConnection("Data Source=testtodos.db");
            });
        });

        return base.CreateHost(builder);
    }
}

在这个示例中,Program class定义了相当多的终端节点:

using System.ComponentModel.DataAnnotations;
using Microsoft.Data.Sqlite;
using Dapper;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("TodoDb") ?? "Data Source=todos.db";
builder.Services.AddScoped(_ => new SqliteConnection(connectionString));
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

await EnsureDb(app.Services, app.Logger);

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/error");
}

app.MapGet("/error", () => Results.Problem("An error occurred.", statusCode: 500))
   .ExcludeFromDescription();

app.MapSwagger();
app.UseSwaggerUI();

app.MapGet("/", () => "Hello World!")
   .WithName("Hello");
...
app.MapDelete("/todos/delete-all", async (SqliteConnection db) => Results.Ok(await db.ExecuteAsync("DELETE FROM Todos")))
    .WithName("DeleteAll")
    .Produces<int>(StatusCodes.Status200OK);

app.Run();

2
@Daan 上周发布了.NET 6 RC1版本,因此现在可以使用WebApplicationFactory<Program> - Panagiotis Kanavos
1
感谢@PanagiotisKanavos指出了Damien的项目。对我来说完美地运作。 - ssilas777
对我来说,在RC1中它不起作用。"“Program”由于其保护级别而无法访问”。问题仍然存在:编译器报错。@PanagiotisKanavos - Daan
1
@Daan,你添加了 InternalsVisibleTo 吗? - Panagiotis Kanavos
2
@Daan 是的,你需要添加 InternalsVisibleTo 测试项目。我在 Web 解决方案的 assemblyInfo 中添加了它。 - ssilas777
显示剩余9条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接