在集成测试 ASP.NET Core Web API 和 EF Core 时重新配置依赖项
Reconfigure dependencies when Integration testing ASP.NET Core Web API and EF Core
我正在学习本教程
Integration Testing with Entity Framework Core and SQL Server
我的代码如下所示
集成测试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
使用我的启动 class
我遇到的问题是当我的 Act 语句被命中时
var response = await _client.GetAsync("/api/route");
我在启动时遇到错误 class,连接字符串为空。我想我对这个问题的理解是,当我的控制器被客户端击中时,它会注入我的数据存储库,而数据存储库又会注入数据库上下文。
我认为我需要将服务配置为 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>();
}
这里有两个选项:
1。使用 WebHostBuilder.ConfigureServices
使用 WebHostBuilder.ConfigureServices
和 WebHostBuilder.UseStartup<T>
来覆盖和模拟网络应用程序的 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
class里面使用TryAdd
方法。自定义 WebHostBuilder.ConfigureServices
在 before 原来的 Startup
之前被调用,所以 mocks 在原来的服务之前注册。 TryAdd
如果相同的接口已经被注册,则不会做任何事情,因此甚至不会触及真正的服务。
更多信息:Running Integration Tests For ASP.NET Core Apps.
2。继承/新启动class
创建 TestStartup
class 以重新配置 ASP.NET 核心 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。
@ilya-chumakov 的回答很棒。我只想再添加一个选项
3。使用 WebHostBuilderExtensions 中的 ConfigureTestServices 方法。
方法 ConfigureTestServices 在 Microsoft.AspNetCore.TestHost 2.1 版中可用(在 2018 年 5 月 20 日是 RC1-final)。它让我们可以用模拟覆盖现有的注册。
代码:
_server = new TestServer(new WebHostBuilder()
.UseStartup<Startup>()
.ConfigureTestServices(services =>
{
services.AddTransient<IFooService, MockService>();
})
);
我正在学习本教程
Integration Testing with Entity Framework Core and SQL Server
我的代码如下所示
集成测试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
使用我的启动 class
我遇到的问题是当我的 Act 语句被命中时
var response = await _client.GetAsync("/api/route");
我在启动时遇到错误 class,连接字符串为空。我想我对这个问题的理解是,当我的控制器被客户端击中时,它会注入我的数据存储库,而数据存储库又会注入数据库上下文。
我认为我需要将服务配置为 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>();
}
这里有两个选项:
1。使用 WebHostBuilder.ConfigureServices
使用 WebHostBuilder.ConfigureServices
和 WebHostBuilder.UseStartup<T>
来覆盖和模拟网络应用程序的 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
class里面使用TryAdd
方法。自定义 WebHostBuilder.ConfigureServices
在 before 原来的 Startup
之前被调用,所以 mocks 在原来的服务之前注册。 TryAdd
如果相同的接口已经被注册,则不会做任何事情,因此甚至不会触及真正的服务。
更多信息:Running Integration Tests For ASP.NET Core Apps.
2。继承/新启动class
创建 TestStartup
class 以重新配置 ASP.NET 核心 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。
@ilya-chumakov 的回答很棒。我只想再添加一个选项
3。使用 WebHostBuilderExtensions 中的 ConfigureTestServices 方法。
方法 ConfigureTestServices 在 Microsoft.AspNetCore.TestHost 2.1 版中可用(在 2018 年 5 月 20 日是 RC1-final)。它让我们可以用模拟覆盖现有的注册。
代码:
_server = new TestServer(new WebHostBuilder()
.UseStartup<Startup>()
.ConfigureTestServices(services =>
{
services.AddTransient<IFooService, MockService>();
})
);