我有一个 IntegrationTestHelper 类,我用它来创建一个带有模拟 InMemory db 的 WebApplicationFactory。
public class IntegrationTestHelper
{
public WebApplicationFactory<RentAPI.Program> GetWebApplicationFactory(string databaseName)
{
var factory = new WebApplicationFactory<RentAPI.Program>().WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
var dbContextDescriptor = services.SingleOrDefault(d =>
d.ServiceType == typeof(DbContextOptions<ApplicationDbContext>));
services.Remove(dbContextDescriptor);
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseInMemoryDatabase(databaseName);
});
});
});
using var scope = factory.Services.CreateScope(); ;
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<User>>();
var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
SeedData(userManager, roleManager, db).Wait();
return factory;
}
...
}
我在下一个测试中使用 GetWebApplicationFactory 来获取 WebApplicationFactory 实例,然后再次使用 WithWebHostBuilder 来模拟 IFileStorageService,但它不起作用。 API 继续使用 Program.cs 中添加的 IFileStorageService 的实现。
public class PropertyControllerTests
{
private IntegrationTestHelper _helper;
private WebApplicationFactory<RentAPI.Program> _factory;
private HttpClient _client;
[SetUp]
public void Setup()
{
_helper = new IntegrationTestHelper();
_factory = _helper.GetWebApplicationFactory(Guid.NewGuid().ToString());
}
#region AddNewProperty
[Test]
public async Task AddNewProperty_ReturnsOk()
{
// Arrange
_factory.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
var fileStorageService = services.SingleOrDefault(d => d.ServiceType == typeof(IFileStorageService));
services.Remove(fileStorageService);
var mockedService = new Mock<IFileStorageService>();
mockedService.Setup(_ => _.UploadFilesAsync(It.IsAny<IFormFile[]>()))
.ReturnsAsync(new List<string> { "fileId1", "fileId2" });
services.AddScoped(_ => mockedService.Object);
});
});
_client = _factory.CreateClient();
... // creating and setting data in content
// Act
var response = await _client.PostAsync("property/landlord/add", content);
// Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
}
[TearDown]
public void TearDown()
{
_client.Dispose();
_factory.Dispose();
}
}
但是当我搬家的时候
var fileStorageService = services.SingleOrDefault(d => d.ServiceType == typeof(IFileStorageService));
services.Remove(fileStorageService);
var mockedService = new Mock<IFileStorageService>();
mockedService.Setup(_ => _.UploadFilesAsync(It.IsAny<IFormFile[]>()))
.ReturnsAsync(new List<string> { "fileId1", "fileId2" });
services.AddScoped(_ => mockedService.Object);
到 GetWebApplicationFactory 一切都开始工作了。看起来像这样
public WebApplicationFactory<RentAPI.Program> GetWebApplicationFactory(string databaseName)
{
var factory = new WebApplicationFactory<RentAPI.Program>().WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
var dbContextDescriptor = services.SingleOrDefault(d =>
d.ServiceType == typeof(DbContextOptions<ApplicationDbContext>));
services.Remove(dbContextDescriptor);
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseInMemoryDatabase(databaseName);
});
var fileStorageService = services.SingleOrDefault(d => d.ServiceType == typeof(IFileStorageService));
services.Remove(fileStorageService);
var mockedService = new Mock<IFileStorageService>();
mockedService.Setup(_ => _.UploadFilesAsync(It.IsAny<IFormFile[]>()))
.ReturnsAsync(new List<string> { "fileId1", "fileId2" });
services.AddScoped(_ => mockedService.Object);
});
});
using var scope = factory.Services.CreateScope(); ;
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<User>>();
var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
SeedData(userManager, roleManager, db).Wait();
return factory;
}
它有效,但对我来说这个实现并不方便。
创建WebApplicationFactory后是否可以模拟服务?
如果您检查
WithWebHostBuilderCore
,您会看到它返回您完全忽略的WebApplicationFactory<TEntryPoint>
。尝试使用它:
var newFactory = _factory.WithWebHostBuilder(builder =>
{
//..
});
var client = newFactory.CreateClient(); // I would recommend to avoid assigning to shared fields in the test
至于“原始”
_factory
- factory.Services
(称为种子)将在内部初始化测试服务器,这需要从服务集合构建服务提供者,从注册的角度来看,默认服务提供者基本上是只读的(即,您可以不添加/删除注册)。