应该模拟数据库连接吗?

Should database connections be mocked?

我正在尝试将单元测试应用于我正在处理的项目。我使用 Python 和 MySQLdb,但我认为此讨论与语言无关。 我想测试一个函数,我们称它为 foobar()。它执行 SQL 类似于此的查询:

cursor.execute("SELECT * FROM my_table WHERE a == " +varA+ " AND  b < " +varB)

这个函数有点复杂。 cursor.execute(query) 的调用方式和频率取决于 foobar 的参数。

现在我不确定是否应该模拟数据库连接 cursor。 如果我不这样做,那就不好了,因为测试取决于数据库服务器的可用性。 如果我这样做了,那就不好了,因为模拟必须知道将调用哪些查询。但确切的查询是一个实现细节。例如。查询是用小写还是大写都不会影响 foobar 的正确性。但是改变它会破坏测试。由于有点复杂的模拟对象,测试也将难以阅读。

这是择善而为的情况吗?还有第三种方法吗?

您已经很好地权衡了这里的利弊,但需要注意的一点是,测试的目的是模拟尽可能接近生产中将看到的确切条件。

您应该避免模拟数据库连接并让它从本地 MysQL 数据库实例读取。此外,确保在每次测试之前重置数据库的状态以确保测试模块化。

一般的工作流程是 -

  1. 在所有测试之前创建架构 myApp_test(如果它尚不存在)。
  2. 删除所有表并从模拟数据装置重新创建它们。您可以通过多种方式执行此操作,我最喜欢的是从 YML 配置文件中读取固定装置并将它们直接插入到数据库中。 (如果您正在使用像 rails 这样的框架,它具有执行此操作的预构建功能,或者有许多其他语言的 gem 和库可能对此有所帮助。)
  3. 每次测试前,调用步骤2中的函数重置DB
  4. 运行 测试

针对您的问题,我不认为这取决于可用的数据库,尤其是在本地。如果您要进行 运行 测试,那么指定您应该有一个可用的数据库并不是没有道理的。或者,如果您真的很关心数据库的可用性,您可以尝试使用 SQLite 或其他基于文本的本地数据库

在本地创建自己的数据库

不,您不应该模拟光标或连接。重构代码并将此代码提取到单独的 service/repository。应该有一个方法需要两个参数 varAvarB 和 returns list of rows/objects

  • 在您的 fast/unit 测试中,您应该模拟整个存储库 - 检查您的方法是否使用正确的参数调用(varAvarB)。
  • 在您的 slow/integration 测试中,您应该检查 您的真实 数据库是否对所有可能的 varAvarB

通过模拟 connection/cursor 你所做的只是实现数据库 - 这只是浪费时间