我有一个标准的ASP.NetWebneneneba API项目,它的DB设置看起来像这样:
启动.cs
services.AddDbContext<ProjectDbContext>(opts =>
opts.UseSqlServer(Configuration.GetConnectionString("ProjectDb")));
这符合预期。
我还有一个集成测试设置,在那里我尝试用内存中的连接来替换SQL Server连接,以便在每次测试运行时都进行一次全新的设置:
ProjectWebApplicationFactory.cs
public class ProjectWebApplicationFactory : WebApplicationFactory<Startup> {
protected override void ConfigureWebHost(IWebHostBuilder builder) {
builder.ConfigureServices(services => {
var prodDb = services.SingleOrDefault(s => s.ServiceType == typeof(ProjectDbContext));
services.Remove(prodDb);
services.AddDbContext<ProjectDbContext>(opts => {
opts.UseInMemoryDatabase(Guid.NewGuid().ToString());
});
}
}
在本地运行时,此操作再次正常工作。然而,当将此代码推送到构建/测试服务器时,它开始失败。初始SQL Server DB上下文尝试在被内存版本替换之前连接到数据库。由于服务器在本地可用,但在测试服务器上不可用,这会导致所有集成测试在运行之前都会失败。
在运行集成测试时,防止SQL Server上下文尝试连接的正确方法是什么?
问题是ProjectWebApplicationFactory.cs中的拼写错误
此行:
var prodDb = services.SingleOrDefault(s => s.ServiceType == typeof(ProjectDbContext));
应该是:
var prodDb = services.SingleOrDefault(s => s.ServiceType == typeof(DbContextOptions<ProjectDbContext>));
SQL服务器数据库在运行集成测试时仍在使用。这导致它不会在启动时失败,而是在实际调用端点时失败。
同样相关的是,内存中的数据库不应使用随机的Guid
字符串创建,而应使用静态值,例如
opts.UseInMemoryDatabase("ProjectDb");
您正在进行集成测试,但不使用项目中的连接字符串。这意味着您已经使用生产数据库设置了API?如果是这样,就不要那样做。
我处理这个问题的方法是使用一些技术,所有这些技术都是为了反转依赖关系而设计的。
- 在具有持久性代码的类的顶部创建一个接口。由于您不太可能有一个类,所以也要创建它并将代码移到上面
- 为单元测试的接口创建mock、stub或fake
- 使用IoC容器交换实现
- 将连接字符串加载移动到持久性类的实例化。这也可以通过IoC容器来完成
这使您可以灵活地在任何地方进行集成测试,并避免在应用程序中嵌入生产字符串(如果您已经这样做的话(。
在这种情况下,你遵循了我经常看到的一些模式,但我发现,当你不了解问题的本质时,这种担忧的混合会导致问题。