C# 在集成测试ASP.NET Core Web API和EF Core时重新配置依赖项

C# 在集成测试ASP.NET Core Web API和EF Core时重新配置依赖项,c#,asp.net-core,integration-testing,entity-framework-core,xunit2,C#,Asp.net Core,Integration Testing,Entity Framework Core,Xunit2,我正在学习本教程 我的代码如下所示 集成测试类 public class ControllerRequestsShould : IDisposable { private readonly TestServer _server; private readonly HttpClient _client; private readonly YourContext _context; public ControllerRequestsShould() {

我正在学习本教程

我的代码如下所示

集成测试类

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();
    }
        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>();
    }
我在startup类中发现连接字符串为空的错误。我认为我对这个问题的理解是,当我的控制器被客户端击中时,它会注入我的数据存储库,而数据存储库又会注入数据库上下文

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

在Startup.cs中配置服务方法

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();
    }
        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>();
    }
public void配置服务(IServiceCollection服务)
{
//添加框架服务
services.AddMvc(setupAction=>
{
setupAction.ReturnHttpNotAcceptable=true;
setupAction.OutputFormatters.Add(新的XmlDataContractSerializerOutputFormatter());
setupAction.InputFormatters.Add(新的XmlDataContractSerializerInputFormatter());
});
//数据库上下文配置
var connectionString=Configuration[“ConnectionStrings:YourConnectionString”];
services.AddDbContext(options=>options.UseSqlServer(connectionString));
//为依赖注入注册服务
services.addScope();
}
这里有两个选项:

1.使用WebHostBuilder.ConfigureServices

使用
WebHostBuilder.ConfigureServices
WebHostBuilder.UseStartup
覆盖和模拟web应用程序的DI注册:

_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>();
    }
}
或者,可以从头开始创建
TestStartup
,以保持测试更干净

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

_server = new TestServer(new WebHostBuilder().UseStartup<TestStartup>());
\u server=newtestserver(new WebHostBuilder().usestaupt());

这是一个完整的大例子:。

@ilya chumakov的答案非常棒。我只想再增加一个选项

3。使用WebHostBuilderExtensions中的ConfigureTestServices方法。

ConfigureTestServices方法在Microsoft.AspNetCore.TestHost 2.1版中提供(2018年5月20日为RC1最终版)。它允许我们用mock覆盖现有的注册

守则:

_server = new TestServer(new WebHostBuilder()
    .UseStartup<Startup>()
    .ConfigureTestServices(services =>
    {
        services.AddTransient<IFooService, MockService>();
    })
);
\u server=newtestserver(new WebHostBuilder()
.UseStartup()
.ConfigureTestServices(服务=>
{
services.AddTransient();
})
);

它如何覆盖
Startup.cs
中的方法“public void ConfigureServices(IServicCollection services)``不是
Virtual
?@gauravaroa,只需创建一个基本方法
Virtual
。它不会破坏任何东西。除非被推入另一个系统,否则它无法解决问题issue@GauravAroraa,问题是甚么?您只需要在
TestStartup
类中执行正确的代码。做这件事的方法并不重要。甚至可以在base
Startup
class中传递变量,这是最好的方法。您还可以删除一些注册,例如
services.RemoveAll(typeof(IHostedService))
在我看到这个答案之前,我正在测试中加入依赖项注入.ConfigureServices。这意味着我必须在实际代码中使用.TryAddTransient,而不是AddTransient。我更喜欢这种方式。因为我更新了.net core 3.0,看起来我需要先删除以前的注册:-(最佳选项,保存日期:D