The*_*man 9 c# integration-testing .net-core
我已经建立了一个基于https://github.com/jasontaylordev/CleanArchitecture的项目。但是我在为控制器编写集成测试时遇到了一些麻烦,因为内存数据库不会在每次测试之间重置。每个测试都使用 WebApplicationFactory 来设置测试 Web 服务器,
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
// Remove the app's ApplicationDbContext registration.
var descriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(DbContextOptions<ApplicationDbContext>));
if (descriptor != null)
{
services.Remove(descriptor);
}
// Add a database context using an in-memory
// database for testing.
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseInMemoryDatabase("InMemoryDbForTesting");
});
// Register test services
services.AddScoped<ICurrentUserService, TestCurrentUserService>();
services.AddScoped<IDateTime, TestDateTimeService>();
services.AddScoped<IIdentityService, TestIdentityService>();
// Build the service provider
var sp = services.BuildServiceProvider();
// Create a scope to obtain a reference to the database
// context (ApplicationDbContext).
using (var scope = sp.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var context = scopedServices.GetRequiredService<ApplicationDbContext>();
var logger = scopedServices.GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();
// Ensure the database is created.
context.Database.EnsureCreated();
try
{
// Seed the database with test data.
SeedSampleData(context);
}
catch (Exception ex)
{
logger.LogError(ex, "An error occurred seeding the database with test messages. Error: {Message}", ex.Message);
}
}
})
.UseEnvironment("Test");
}
...
Run Code Online (Sandbox Code Playgroud)
其中以下创建测试:
namespace CleanArchitecture.WebUI.IntegrationTests.Controllers.TodoItems
{
public class Create : IClassFixture<CustomWebApplicationFactory<Startup>>
{
private readonly CustomWebApplicationFactory<Startup> _factory;
public Create(CustomWebApplicationFactory<Startup> factory)
{
_factory = factory;
}
[Fact]
public async Task GivenValidCreateTodoItemCommand_ReturnsSuccessCode()
{
var client = await _factory.GetAuthenticatedClientAsync();
var command = new CreateTodoItemCommand
{
Title = "Do yet another thing."
};
var content = IntegrationTestHelper.GetRequestContent(command);
var response = await client.PostAsync($"/api/todoitems", content);
response.EnsureSuccessStatusCode();
}
...
Run Code Online (Sandbox Code Playgroud)
以及以下读取测试:
namespace CleanArchitecture.WebUI.IntegrationTests.Controllers.TodoItems
{
public class Read: IClassFixture<CustomWebApplicationFactory<Startup>>
{
private readonly CustomWebApplicationFactory<Startup> _factory;
public Read(CustomWebApplicationFactory<Startup> factory)
{
_factory = factory;
}
[Fact]
public async Task ShouldRetriveAllTodos()
{
var client = await _factory.GetAuthenticatedClientAsync();
var response = await client.GetAsync($"/api/todoitems");
var todos = Deserialize(response);
todos.Count().Should().Be(1); //but is 2 because database is shared between read and create test class.
}
Run Code Online (Sandbox Code Playgroud)
问题是内存数据库在每次测试之间不会重置。我尝试使用 为内存数据库生成不同的名称Guid.New.ToString,但随后测试找不到种子数据库数据,并且将测试放在同一个 XUnit 集合中,但无济于事。
如何使测试不共享数据库有什么好主意吗?
对我有用的是为每个实例生成一个 DBName WebApplicationFactory,然后我为每个测试实例化其中一个。所以测试看起来像这样:
[Fact]
public void Test()
{
// Arrange
var appFactory = new WebApplicationFactory();
// ...
// Act
// ...
// Assert
// ...
}
Run Code Online (Sandbox Code Playgroud)
还有WebApplicationFactory:
public class TestWebApplicationFactory : WebApplicationFactory<Startup>
{
private readonly string _dbName = Guid.NewGuid().ToString();
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
base.ConfigureWebHost(builder);
builder.ConfigureServices(services =>
{
services.AddDbContext<DbContext>(options =>
{
// This is what makes a unique in-memory database per instance of TestWebApplicationFactory
options.UseInMemoryDatabase(_dbName);
});
});
}
}
Run Code Online (Sandbox Code Playgroud)
创建 DbContext 时,我们通过以下方式将测试彼此隔离:
private static DbContextOptions<ApplicationDbContext> CreateDbContextOptions()
{
return new DbContextOptionsBuilder<ApplicationDbContext>()
//Database with same name gets reused, so let's isolate the tests from each other...
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
}
Run Code Online (Sandbox Code Playgroud)
在测试类中:
using (var context = new ApplicationDbContext(DbContextTestHelper.CreateDbContextOptions())
{
//arrange data in context
var testee = new XY(context);
//do test
//do asserts
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
8195 次 |
| 最近记录: |