【问题标题】:Reconfigure dependencies when Integration testing ASP.NET Core Web API and EF Core集成测试 ASP.NET Core Web API 和 EF Core 时重新配置依赖项
【发布时间】:2017-04-21 12:57:05
【问题描述】:

我正在关注本教程
Integration Testing with Entity Framework Core and 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 使用我的启动类

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

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

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

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

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

【问题讨论】:

    标签: c# asp.net-core integration-testing entity-framework-core xunit2


    【解决方案1】:

    @ilya-chumakov 的回答很棒。我只是想再添加一个选项

    3.使用 WebHostBuilderExtensions 中的 ConfigureTestServices 方法。

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

    代码:

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

    【讨论】:

    • 最好的方法。你也可以删除一些注册,例如services.RemoveAll(typeof(IHostedService))
    • 在我看到这个答案之前,我将我的依赖注入在我的测试中的 .ConfigureServices 中。这意味着我必须在实际代码中使用 .TryAddTransient 而不是 AddTransient。我更喜欢这种方式。
    • 由于我更新到.net core 3.0,看来我需要先删除以前的注册:-(
    • 最佳选择,拯救了这一天:D
    【解决方案2】:

    这里有两个选项:

    1。使用WebHostBuilder.ConfigureServices

    使用 WebHostBuilder.ConfigureServices 和 WebHostBuilder.UseStartup&lt;T&gt; 来覆盖和模拟 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>();
        }
    }
    

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

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

    2。继承/新建 Startup 类

    创建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>());
    

    这是一个完整的大示例:Integration testing your asp .net core app with an in memory database。

    【讨论】:

    • 它如何覆盖 - Startup.cs 中的方法 ``` public void ConfigureServices(IServiceCollection services)``` 不是 Virtual ?
    • @GauravAroraa,只需创建一个基本方法virtual。它不会破坏任何东西。
    • 它没有解决问题,除非被推到另一个问题中
    • @GauravAroraa,有什么问题?您只需要在TestStartup 类中执行正确的代码。做到这一点的方法并不重要。甚至可以在基类Startup 类stackoverflow.com/a/44486236/5112433 中传递一个变量
    猜你喜欢
    • 2016-11-22
    • 1970-01-01
    • 1970-01-01
    • 2018-11-13
    • 2018-03-24
    • 1970-01-01
    • 2018-12-31
    • 1970-01-01
    • 2020-01-27
    相关资源
    最近更新 更多