首页 文章

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

提问于
浏览
8

我正在学习本教程
Integration Testing with Entity Framework Core and SQL Server

我的代码看起来像这样

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

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

我遇到的问题是当我的法案声明被击中时

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

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

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

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

2 回答

  • 6

    1.使用WebHostBuilder.ConfigureServices

    过了一段时间,我认为最简单的解决方案是使用 WebHostBuilder.ConfigureServicesWebHostBuilder.UseStartup<T> 来覆盖和模拟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 the service
            services.TryAddTransient<IFooService, FooService>();
        }
    }
    

    这里的关键是在 Startup class中使用 TryAdd 方法 . WebHostBuilder.ConfigureServices 被称为 before 原始 Startup ,所以首先注册你的模拟 . 如果使用 TryAdd ,则跳过实际服务的注册 .

    更多信息:Running Integration Tests For ASP.NET Core Apps .

    2.继承/新的启动类

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

    public class TestStartup : Startup
    {
        public TestStartup(IHostingEnvironment env) : base(env) { }
    
        public override void ConfigureServices(IServiceCollection services)
        {
            //mock DbContext and any other dependencies here
        }
    }
    

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

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

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

    Сomplete大例子:Integration testing your asp .net core app with an in memory database .

  • 12

    @ ilya-chumakov的答案很棒 . 我只想补充一个选项

    3. Use ConfigureTestServices method from WebHostBuilderExtensions.

    方法ConfigureTestServices在Microsoft.AspNetCore.TestHost版本2.1中可用(在20.05.2018,它是RC1-final) . 它让我们可以用模拟覆盖现有的注册 .

    代码:

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

相关问题