Database 应该模拟数据库连接吗?

Database 应该模拟数据库连接吗?,database,testing,mocking,Database,Testing,Mocking,我正在尝试将单元测试应用到我正在从事的项目中。我使用Python和MySQLdb,但我认为这个讨论与语言无关。 我想测试一个函数,我们称它为foobar()。它执行类似以下内容的SQL查询: cursor.execute("SELECT * FROM my_table WHERE a == " +varA+ " AND b < " +varB) cursor.execute(“从my_表中选择*,其中a==”+varA+“和b

我正在尝试将单元测试应用到我正在从事的项目中。我使用Python和MySQLdb,但我认为这个讨论与语言无关。 我想测试一个函数,我们称它为foobar()。它执行类似以下内容的SQL查询:

cursor.execute("SELECT * FROM my_table WHERE a == " +varA+ " AND  b < " +varB)
cursor.execute(“从my_表中选择*,其中a==”+varA+“和b<”+varB)
函数中有一点复杂。 调用
cursor.execute(query)
的方式和频率取决于foobar的参数

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


这是一个选择较小邪恶的案例吗?还有第三种方法吗?

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

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

一般的工作流程是-

  • 在所有测试之前,创建模式
    myApp\u test
    (如果它不存在)
  • 删除所有表并从模拟数据装置重新创建它们。您可以通过几种方法来实现这一点,我最喜欢的方法是从YML配置文件中读取装置,并将其直接插入数据库中。(如果您使用的是像rails这样的框架,它有预构建的功能来实现这一点,或者有许多其他语言的gem和库可以帮助您实现这一点。)
  • 每次测试前,调用步骤2中的函数重置DB
  • 运行测试

  • 针对您的担忧,我不认为这取决于数据库的可用性,特别是本地可用性。如果您正在运行测试,那么指定应该有一个可用的DB并不是不合理的。或者,如果您确实关心数据库的可用性,您可以尝试使用SQLite或其他基于文本的本地数据库在本地创建自己的数据库。否,您不应该模拟光标或连接。重构代码并将此代码提取到单独的服务/存储库中。应该有一个方法,它接受两个参数
    varA
    varB
    ,并返回行/对象列表

    • 在您的fast/单元测试中,您应该模拟整个存储库-检查是否使用正确的参数调用了您的方法(
      varA
      varB
    • 在慢速/集成测试中,您应该检查real数据库是否正确执行了所有可能的
      varA
      varB

    通过模拟连接/游标,您所做的只是实现数据库-这只是浪费时间

    只有在主题这么大的时候才有这么一个小问题?对真实数据库运行单元测试(即使它是为测试目的创建的本地数据库)会使它们变得缓慢而脆弱。