在 ASP.Net 集成测试中阻止 SQL 服务器连接
Prevent SQL Server Connection in ASP.Net Integration test
我有一个标准的 ASP.Net Web API 项目,其数据库设置如下所示:
Startup.cs
services.AddDbContext<ProjectDbContext>(opts =>
opts.UseSqlServer(Configuration.GetConnectionString("ProjectDb")));
这按预期工作。
我还有一个集成测试设置,我尝试用内存中的服务器连接替换 SQL 服务器连接,以便每次测试都保持干净状态 运行:
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());
});
}
}
这在本地 运行ning 时再次正常工作。但是,当将此代码推送到构建/测试服务器时,它开始失败。初始 SQL 服务器数据库上下文在被内存版本替换之前尝试连接到数据库。由于服务器在本地可用,但在测试服务器上不可用,这会导致所有集成测试失败,甚至无法 运行.
什么是防止 SQL 服务器上下文在 运行 进行集成测试时尝试连接的正确方法?
您正在进行集成测试,但未使用您项目中的连接字符串。这意味着您的 API 设置了您的生产数据库?如果是这样,请不要那样做。
我处理这个问题的方法是使用一些技术,所有这些技术都是为了反转依赖关系而设计的。
- 在具有持久性代码的 class 之上创建一个接口。由于您可能没有 class,因此也制作它并将代码移至
- 为您的界面创建模拟、存根或伪造以进行单元测试
- 使用 IoC 容器替换实现
- 将连接字符串加载移动到持久性实例化 class。这也可以通过 IoC 容器来完成
这使您可以灵活地在任何需要的地方进行集成测试,并避免在您的应用程序中嵌入生产字符串(如果您这样做的话)。
在这种情况下,您遵循的是我经常看到的一些模式,但我发现当您不了解问题的本质时,这种混合关注最终会导致问题。
问题是 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");
我有一个标准的 ASP.Net Web API 项目,其数据库设置如下所示:
Startup.cs
services.AddDbContext<ProjectDbContext>(opts =>
opts.UseSqlServer(Configuration.GetConnectionString("ProjectDb")));
这按预期工作。
我还有一个集成测试设置,我尝试用内存中的服务器连接替换 SQL 服务器连接,以便每次测试都保持干净状态 运行:
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());
});
}
}
这在本地 运行ning 时再次正常工作。但是,当将此代码推送到构建/测试服务器时,它开始失败。初始 SQL 服务器数据库上下文在被内存版本替换之前尝试连接到数据库。由于服务器在本地可用,但在测试服务器上不可用,这会导致所有集成测试失败,甚至无法 运行.
什么是防止 SQL 服务器上下文在 运行 进行集成测试时尝试连接的正确方法?
您正在进行集成测试,但未使用您项目中的连接字符串。这意味着您的 API 设置了您的生产数据库?如果是这样,请不要那样做。
我处理这个问题的方法是使用一些技术,所有这些技术都是为了反转依赖关系而设计的。
- 在具有持久性代码的 class 之上创建一个接口。由于您可能没有 class,因此也制作它并将代码移至
- 为您的界面创建模拟、存根或伪造以进行单元测试
- 使用 IoC 容器替换实现
- 将连接字符串加载移动到持久性实例化 class。这也可以通过 IoC 容器来完成
这使您可以灵活地在任何需要的地方进行集成测试,并避免在您的应用程序中嵌入生产字符串(如果您这样做的话)。
在这种情况下,您遵循的是我经常看到的一些模式,但我发现当您不了解问题的本质时,这种混合关注最终会导致问题。
问题是 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");