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

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

问题描述

我正在关注本教程

与Entity Framework Core和SQL Server的集成测试



我的代码看起来像这样



集成测试类

 公共类ControllerRequestsShould :IDisposable 
{
私人只读TestServer _server;
私有只读HttpClient _client;
private readonly YourContext _context;

public ControllerRequestsShould()
{
//安排
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();
}

[事实]
公共异步任务ReturnListOfObjectDtos()
{
//排列数据库数据
_context.ObjectDbSet.Add(新的ObjectEntity {Id = 1,代码= PTF0001,名称=投资组合一});
_context.ObjectDbSet.Add(new ObjectEntity {ID = 2,Code = PTF0002,Name = Portfolio Two});

//操作
var response = await _client.GetAsync( / api / route);
response.EnsureSuccessStatusCode();


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

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

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



我遇到的问题是我的Act语句被击中

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

启动类中出现错误,提示连接字符串为空。我认为我对该问题的理解是,当从客户端命中我的控制器时,它会注入我的数据存储库,这又会注入数据库上下文。



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



Startup.cs中的ConfigureServices方法

  public void ConfigureServices(IServiceCollection服务)
{
//添加框架服务
services.AddMvc(setupAction =>
{
setupAction.ReturnHttpNotAcceptable = true;
setupAction.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
setupAction.InputFormatters.Add(new XmlDataContractSerializerInputFormatter());
});

// Db上下文配置
var connectionString = Configuration [ ConnectionStrings:YourConnectionString];
services.AddDbContext< YourContext>(options => options.UseSqlServer(connectionString));

//注册用于依赖项注入的服务
services.AddScoped< IYourRepository,YourRepository>();
}


解决方案

以下是两个选择: / p>

1。使用 WebHostBuilder.ConfigureServices


使用 WebHostBuilder.ConfigureServices WebHostBuilder.UseStartup< T> 覆盖并模拟Web应用程序的DI注册:

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

公共类Startup
{
public void ConfigureServices(IServiceCollection services)
{
//使用TryAdd来支持IFooService
服务的模拟。 TryAddTransient< IFooService,FooService>();
}
}

此处的关键是使用 TryAdd 原始 Startup 类中的方法。自定义 WebHostBuilder.ConfigureServices 在原始的启动之前被称为之前,因此模拟在注册之前原始的服务。如果已经注册了相同的接口, TryAdd 不会执行任何操作,因此甚至不会触摸真实的服务。


更多信息:针对ASP的运行集成测试。 NET Core应用


2。继承/新的Startup类


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

 公共类TestStartup:启动
{
public TestStartup(IHostingEnvironment env):base(env){}

public overlay void ConfigureServices(IServiceCollection services)
{
//模拟DbContext和此处的任何其他依赖项
}
}

或者 TestStartup 可以从头开始创建,以保持测试的整洁。


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

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

这是一个完整的大型示例:将您的asp .net核心应用程序与内存数据库进行集成测试


I'm following this tutorial
Integration Testing with Entity Framework Core and SQL Server

My code looks like this

Integration Test Class

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();
    }

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

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

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

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.

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.

ConfigureServices method in Startup.cs

        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>();
    }

解决方案

Here are two options:

1. Use WebHostBuilder.ConfigureServices

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>();
    }
}

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.

More info: Running Integration Tests For ASP.NET Core Apps.

2. Inheritance / new Startup class

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
    }
}

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

And specify it in UseStartup to run the test server:

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

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天全站免登陆