【问题标题】:Prevent SQL Server Connection in ASP.Net Integration test在 ASP.Net 集成测试中阻止 SQL Server 连接
【发布时间】:2021-04-15 22:15:36
【问题描述】:

我有一个标准的 ASP.Net Web API 项目,其数据库设置如下所示:

Startup.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 数据库上下文在被替换为内存版本之前尝试连接到数据库。由于服务器在本地可用,但在测试服务器上不可用,这会导致所有集成测试在无法运行之前就失败。

在运行集成测试时防止 SQL Server 上下文尝试连接的正确方法是什么?

【问题讨论】:

  • 除非您需要,否则不要致电UseSqlServer。至少,在选项操作中添加一个if 块并添加您真正想要的提供程序。
  • 应用程序的正常运行需要它。我不想唯一一次是在集成测试期间。在 Startup 类中没有 if
  • 所以添加一个if,这仅在单元测试期间是正确的。或者根本不要在单元测试期间使用 Startup,在单元测试本身中创建一个 ServicesCollection 实例。 AddDbContext 接受一个动作。你可以写AddDbContext(opts=&gt;{ if (...){opts.UseSqlServer...} else { opts.UseInMemory,而不是写AddDbContext(opts=&gt;opts.Use....。这要容易得多。用你想要的配置创建一个ServiceCollection

标签: c# asp.net sql-server asp.net-web-api integration-testing


【解决方案1】:

问题是 ProjectWebApplicationFactory.cs

中的拼写错误

这一行:

var prodDb = services.SingleOrDefault(s => s.ServiceType == typeof(ProjectDbContext));

应该是:

var prodDb = services.SingleOrDefault(s => s.ServiceType == typeof(DbContextOptions<ProjectDbContext>));

运行集成测试时仍在使用 SQL Sever DB。这导致它不是在启动时失败,而是在实际调用端点时失败。

同样相关的是,内存数据库不应使用随机的Guid 字符串创建,而应使用静态值,例如

opts.UseInMemoryDatabase("ProjectDb");

【讨论】:

    【解决方案2】:

    您正在进行集成测试,但不是在您的项目中使用连接字符串。这意味着您已经使用生产数据库设置了 API?如果是这样,请不要这样做。

    我处理这个问题的方法是使用几种技术,所有这些技术都旨在反转依赖关系。

    1. 在具有持久性代码的类之上创建一个接口。由于您可能没有课程,所以也制作课程并将代码移到上面
    2. 为您的接口创建模拟、存根或伪造品以进行单元测试
    3. 使用 IoC 容器替换实现
    4. 将连接字符串加载移动到持久性类的实例化。这也可以通过 IoC 容器来完成

    这使您可以灵活地在任何地方进行集成测试,并避免将生产字符串嵌入到您的应用程序中(如果您已经这样做了)。

    在这种情况下,您会遵循一些我经常看到的模式,但我发现当您不了解问题的性质时,这种混合关注会导致问题。

    【讨论】:

    • OP 已经在使用 DI、IoC 和所有这些模式。真正的问题是代码试图在单元测试中使用用于生产的 DI 配置
    猜你喜欢
    • 2022-01-25
    • 2016-05-18
    • 1970-01-01
    • 2011-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-02
    • 1970-01-01
    相关资源
    最近更新 更多