集成测试时将自定义的DbContext注入Asp.NET控制器

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

我有以下简化的 ASP.net API 控制器来测试:

[Route("api")]
public class CustomerController(Func<DataContext> factory) : Controller
{
    [HttpPost("customer")]
    public async Task<IActionResult> Create([FromBody] CustomerRequest request)
    {
        using var context = factory.Invoke();
        var uow = new UnitOfWork(context);

        await uow.CustomerService.Create(request.ToCustomer());

        return Ok();
    }
}

下面是我的测试:

[Collection(nameof(ControllerTestCollection))]
public class CustomerApiTest(IntegrationTestFactory factory)
{
   private readonly HttpClient client = factory.CreateClient();

    [Fact]
    public async Task TestCreateCustomer()
    {
        var response = await client.PostAsJsonAsync("api/customer", new CustomerRequest(1, "Customer 1"));

        response.StatusCode.Should().Be(HttpStatusCode.Created);
    }
}

下面是我定制的WebApplicationFactory:

protected override void ConfigureWebHost(IWebHostBuilder builder)
{
    builder.ConfigureTestServices
    (
        services => 
        {
           services.RemoveDbContext<DataContext>();
           services.AddDbContext<DataContext>(options => options.UseMySql(testcontainer.GetConnectionString()));
           services.EnsureDbCreated<DataContext>();
        }
    );
}

当我运行测试时,控制器继续使用真实的数据库设置,而不是我为此测试设置的设置,因此基本上它会忽略指向 Testcontainers 托管的数据库的 DataContext。但我可以保证传递给测试的自定义 WebApplicationFactory 正确指向测试数据库。

我是 ASP.Net 和 EF Core 的新手,因此我们将不胜感激。

致以诚挚的问候,

设置Nug

asp.net-web-api entity-framework-core integration-testing xunit.net
1个回答
0
投票

WebApplicationFactory<TEntryPoint>
用于创建用于集成测试的 TestServer。 TEntryPoint 是 SUT 的入口点类,通常是 Program.cs。

我已经在本地进行了测试,正确传递了数据上下文

自定义WebApplicationFactory

using System.Data.Common;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;

public class CustomWebApplicationFactory<TProgram> : WebApplicationFactory<TProgram> where TProgram : class
{
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureServices(services =>
        {
            var dbContextDescriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<DataContext>));
            services.Remove(dbContextDescriptor!);

            var dbConnectionDescriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbConnection));
            services.Remove(dbConnectionDescriptor!);

            // Create open SqliteConnection so EF won't automatically close it.
            services.AddSingleton<DbConnection>(container =>
            {
                var connection = new SqliteConnection("DataSource=:memory:");
                connection.Open();

                return connection;
            });

            services.AddDbContext<DataContext>((container, options) =>
            {
                var connection = container.GetRequiredService<DbConnection>();
                options.UseSqlite(connection);
            });
        });

        builder.UseEnvironment("Development");
    }
} 

测试

public class CustomerApiTest(CustomWebApplicationFactory<Program> factory) : **IClassFixture<CustomWebApplicationFactory<Program>>**
{
    private readonly HttpClient client = factory.CreateClient();

    [Fact]
    public async Task TestCreateCustomer()
    {
        var response = await client.GetAsync("/WeatherForecast");
    }
}

数据库上下文

using Microsoft.EntityFrameworkCore;

public class DataContext(DbContextOptions<DataContext> options) : DbContext(options)
{
    public virtual DbSet<Message> Messages { get; set; }

}

控制器

using Microsoft.AspNetCore.Mvc;

namespace WebApplication.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController(ILogger<WeatherForecastController> logger, DataContext context) : ControllerBase
    {
        private static readonly string[] Summaries =
        [
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        ];

        private readonly ILogger<WeatherForecastController> _logger = logger;
        private readonly DataContext context = context;

        [HttpGet(Name = "GetWeatherForecast")]
        public IEnumerable<WeatherForecast> Get()
        {
            var messages = context.Messages.ToList();

            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
            })
            .ToArray();
        }
    }
}

Debug

Microsoft 文档: https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-8.0#customize-webapplicationfactory

class-fixturehttps://xunit.net/docs/shared-context#class-fixture

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