集成测试 ASP.NET Core Web API 和 EF Core 时重新配置依赖项 [英] Reconfigure dependencies when Integration testing ASP.NET Core Web API and EF Core

查看:22
本文介绍了集成测试 ASP.NET Core Web API 和 EF Core 时重新配置依赖项的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在学习本教程
集成使用 Entity Framework Core 和 SQL Server 进行测试

我的代码是这样的

集成测试类

public class ControllerRequestsShould : IDisposable
{
    private readonly TestServer _server;
    private readonly HttpClient _client;
    private readonly YourContext _context;

    public ControllerRequestsShould()
    {
        // Arrange
        var serviceProvider = new ServiceCollection()
            .AddEntityFrameworkSqlServer()
            .BuildServiceProvider();

        var builder = new DbContextOptionsBuilder<YourContext>();

        builder.UseSqlServer($"Server=(localdb)\mssqllocaldb;Database=your_db_{Guid.NewGuid()};Trusted_Connection=True;MultipleActiveResultSets=true")
            .UseInternalServiceProvider(serviceProvider);

        _context = new YourContext(builder.Options);
        _context.Database.Migrate();

        _server = new TestServer(new WebHostBuilder()
            .UseStartup<Startup>()
            .UseEnvironment(Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")));
        _client = _server.CreateClient();
    }

    [Fact]
    public async Task ReturnListOfObjectDtos()
    {
        // Arrange database data
        _context.ObjectDbSet.Add(new ObjectEntity{ Id = 1, Code = "PTF0001", Name = "Portfolio One" });
        _context.ObjectDbSet.Add(new ObjectEntity{ Id = 2, Code = "PTF0002", Name = "Portfolio Two" });

        // Act
        var response = await _client.GetAsync("/api/route");
        response.EnsureSuccessStatusCode();


        // Assert
        var result = Assert.IsType<OkResult>(response);            
    }

    public void Dispose()
    {
        _context.Dispose();
    }

据我所知,.UseStartUp 方法确保 TestServer 使用我的启动类

As I understand it, the .UseStartUp method ensures the TestServer uses my startup class

我遇到的问题是,当我的 Act 语句被点击时

The issue I'm having is that when my Act statement is hit

var response = await _client.GetAsync("/api/route");

我在启动类中收到一个错误,即连接字符串为空.我想我对这个问题的理解是,当我的控制器被客户端击中时,它会注入我的数据存储库,进而注入 db 上下文.

I get an error in my startup class that the connection string is null. I think My understanding of the problem is that when my controller is hit from the client it injects my data repository, which in turn injects the db context.

我想我需要将服务配置为 new WebHostBuilder 部分的一部分,以便它使用在测试中创建的上下文.但我不知道该怎么做.

I think I need to configure the service as part of the new WebHostBuilder section so that it used the context created in the test. But I'm not sure how to do this.

Startup.cs 中的ConfigureServices 方法

        public void ConfigureServices(IServiceCollection services)
    {
        // Add framework services
        services.AddMvc(setupAction =>
        {
            setupAction.ReturnHttpNotAcceptable = true;
            setupAction.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
            setupAction.InputFormatters.Add(new XmlDataContractSerializerInputFormatter());
        });

        // Db context configuration
        var connectionString = Configuration["ConnectionStrings:YourConnectionString"];
        services.AddDbContext<YourContext>(options => options.UseSqlServer(connectionString));

        // Register services for dependency injection
        services.AddScoped<IYourRepository, YourRepository>();
    }

推荐答案

这里有两个选项:

使用 WebHostBuilder.ConfigureServicesWebHostBuilder.UseStartup 来覆盖和模拟 Web 应用程序的 DI 注册:

Use WebHostBuilder.ConfigureServices together with WebHostBuilder.UseStartup<T> to override and mock a web application`s DI registrations:

_server = new TestServer(new WebHostBuilder()
    .ConfigureServices(services =>
    {
        services.AddScoped<IFooService, MockService>();
    })
    .UseStartup<Startup>()
);

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        //use TryAdd to support mocking IFooService
        services.TryAddTransient<IFooService, FooService>();
    }
}

这里的关键是在原始Startup 类中使用TryAdd 方法.自定义 WebHostBuilder.ConfigureServices 在原始 Startup 之前被调用,因此模拟在原始服务之前注册.TryAdd 如果已经注册了相同的接口,则不会做任何事情,因此甚至不会触及真正的服务.

The key point here is to use TryAdd methods inside the original Startup class. Custom WebHostBuilder.ConfigureServices is called before the original Startup, so the mocks are registered before the original services. TryAdd doesn't do anything if the same interface has already been registered, thus the real services will not be even touched.

更多信息:运行集成ASP.NET Core 应用程序测试.

创建 TestStartup 类以重新配置 ASP.NET Core DI.您可以从 Startup 继承它并仅覆盖需要的方法:

Create TestStartup class to re-configure ASP.NET Core DI. You can inherit it from Startup and override only needed methods:

public class TestStartup : Startup
{
    public TestStartup(IHostingEnvironment env) : base(env) { }

    public override void ConfigureServices(IServiceCollection services)
    {
        //mock DbContext and any other dependencies here
    }
}

或者 TestStartup 可以从头开始创建以保持测试更清晰.

Alternatively TestStartup can be created from scratch to keep testing cleaner.

并在UseStartup中指定运行测试服务器:

And specify it in UseStartup to run the test server:

_server = new TestServer(new WebHostBuilder().UseStartup<TestStartup>());

这是一个完整的大例子:使用内存数据库集成测试您的 asp .net 核心应用.

This is a complete large example: Integration testing your asp .net core app with an in memory database.

这篇关于集成测试 ASP.NET Core Web API 和 EF Core 时重新配置依赖项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆