WebApplicationFactory无法为InMemoryDatabase测试设置初始数据
问题原因及解决方案
核心问题1:请求指向了真实API而非测试模拟主机
你在测试中使用的baseURLString = "https://localhost:7228/"是真实运行的API地址,而WebApplicationFactory.CreateClient()返回的HttpClient已经自动配置了测试模拟主机的基地址,你的请求实际发送到了外部API,而非测试用的模拟应用,自然看不到测试数据。
核心问题2:数据初始化在临时服务容器中
在ConfigureWebHost的ConfigureServices方法中调用services.BuildServiceProvider()会创建一个独立的临时服务容器,你在这里初始化的数据只存在于这个临时容器的内存数据库中,而测试应用实际使用的是后续构建的正式服务容器,两者上下文不共享(即使内存数据库名称相同,这种提前构建的方式也会导致状态不一致)。
修正后的代码
1. 修正测试基类BaseTestInitialziacion
移除硬编码的真实地址,直接使用相对路径访问测试主机:
namespace UnitTests_EternalCODEStudio_API.TestData2 { public class BaseTestInitialziacion { // 不需要指定真实地址,HttpClient已内置测试主机基地址 protected string baseURLString = ""; MyWebApplication WebApp = null!; protected HttpClient httpClient = null!; [TestInitialize] public void Setup() { WebApp = new MyWebApplication(); httpClient = WebApp.CreateClient(); } } }
2. 修正MyWebApplication类
将数据初始化逻辑移到CreateHost方法中,使用主机的正式服务容器:
class MyWebApplication : WebApplicationFactory<Program> { protected override IHost CreateHost(IHostBuilder builder) { var host = base.CreateHost(builder); // 使用主机的服务容器初始化数据 using(var scope = host.Services.CreateScope()) { var dbContext = scope.ServiceProvider.GetRequiredService<ECSContext>(); dbContext.Database.EnsureDeleted(); dbContext.Database.EnsureCreated(); dbContext.TUsers.AddRange(TestDataProvider.GetSampleUsers()); dbContext.TRoles.AddRange(TestDataProvider.GetSampleRoles()); dbContext.TUsersroles.AddRange(TestDataProvider.GetSampleUsersAndRoles()); int changesMade = dbContext.SaveChanges(); Console.WriteLine($"ChangesMade: {changesMade}"); } return host; } protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.ConfigureServices(services => { // 移除原有数据库配置 services.RemoveAll<DbContextOptions<ECSContext>>(); // 添加内存数据库,使用固定或随机名称均可 services.AddDbContext<ECSContext>(builder => { builder.UseInMemoryDatabase(databaseName: "TestECSDb"); // 固定名称便于调试,也可以用Guid.NewGuid().ToString() }); }); } }
3. 修正测试方法中的请求路径
确保使用相对路径:
[TestMethod] public async Task Test_UserController_Login_OK() { UsersDTO_usernameAndPass userData = new UsersDTO_usernameAndPass { Username = "user1", Password = "Test!Password1" }; StringContent jsonContent = new StringContent(JsonConvert.SerializeObject(userData), Encoding.UTF8, "application/json"); // 使用相对路径,HttpClient会自动拼接测试主机基地址 var response = await httpClient.PostAsync($"{baseURLString}Users/Login", jsonContent); Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); }
额外说明
- 内存数据库是按名称隔离的,同一个名称的数据库会被不同的
ECSContext实例共享,所以固定名称有助于调试,使用随机名称则可以保证每个测试实例的数据库独立。 - 移除
dbContext.ChangeTracker.Clear(),因为在初始化数据前上下文是全新的,不需要清除跟踪器,反而可能导致数据保存异常。
内容的提问来源于stack exchange,提问作者Alex Ibrahim Ojea
相关产品推荐
相关产品推荐

