23

我正在关注本教程
使用 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使用我的启动类

我遇到的问题是,当我的 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>();
    }
4

2 回答 2

41

@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>();
    })
);
于 2018-05-20T11:48:45.377 回答
27

这里有两个选项:

1.使用WebHostBuilder.ConfigureServices

WebHostBuilder.ConfigureServices与 一起使用来WebHostBuilder.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 IFooService
        services.TryAddTransient<IFooService, FooService>();
    }
}

这里的关键是使用TryAdd原始Startup类中的方法。Custom在 original之前WebHostBuilder.ConfigureServices调用,因此 mocks 在原始服务之前注册。如果已经注册了相同的接口,则不执行任何操作,因此甚至不会触及真正的服务。StartupTryAdd

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

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

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

于 2017-04-21T20:24:04.517 回答