对依赖于数据库的函数进行单元测试

Sal*_*ikh 0 python unit-testing database-testing python-unittest

我正在对某些功能进行测试。我有一个使用数据库查询的函数。因此,我浏览了博客和文档,这些博客和文档说我们必须创建一个内存或测试数据库才能使用此类功能。以下是我的功能,

def already_exists(story_data,c):
    # TODO(salmanhaseeb): Implement de-dupe functionality by checking if it already
    # exists in the DB.
    c.execute("""SELECT COUNT(*) from posts where post_id = ?""", (story_data.post_id,))
    (number_of_rows,)=c.fetchone()
    if number_of_rows > 0:
        return True
    return False
Run Code Online (Sandbox Code Playgroud)

该函数访问生产数据库。我的问题是,在测试时,我创建一个内存数据库并在那里填充我的值,我将查询该数据库(测试数据库)。但我想测试我的already_exists()函数,从测试调用我的already_exists函数后,我的生产数据库将被命中。在测试此功能时如何使我的测试数据库命中?

jpm*_*c26 5

您可以采取两种途径来解决此问题:

  1. 进行集成测试而不是单元测试,并仅使用真实数据库的副本。
  2. 为该方法提供一个假值,而不是实际的连接对象。

您应该做什么取决于您想要实现的目标。

如果您想测试查询本身是否有效,那么您应该使用集成测试。句号。确保查询符合预期的唯一方法是使用数据库副本中已有的测试数据来运行查询。针对不同的数据库技术运行它(例如,当您的生产数据库位于 PostgreSQL 中时针对 SQLite 运行)将无法确保它在生产中正常工作。需要数据库的副本意味着您将需要一些自动化的部署过程,可以轻松地针对单独的数据库调用该过程。无论如何,您应该拥有这样的自动化流程,因为它有助于确保跨环境的部署保持一致,允许您在发布之前对其进行测试,并“记录”升级数据库的过程。标准解决方案是用您的编程语言编写的迁移工具(如albemic)或执行原始 SQL 的工具(如yoyoFlyway)。您需要在运行测试之前调用部署并用测试数据填充它,然后运行测试并断言您期望返回的输出。

如果您想测试查询周围的代码而不是查询本身,那么您可以对连接对象使用假值。最常见的解决方案是模拟。模拟提供了可以配置为接受函数调用和输入并返回一些输出来代替真实对象的替身。假设查询返回您期望的结果,这将允许您测试该方法的逻辑是否正常工作。对于您的方法,这样的测试可能如下所示:

from unittest.mock import Mock

...

def test_already_exists_returns_true_for_positive_count():
    mockConn = Mock(
        execute=Mock(),
        fetchone=Mock(return_value=(5,)),
    )
    story = Story(post_id=10) # Making some assumptions about what your object might look like.

    result = already_exists(story, mockConn)

    assert result

    # Possibly assert calls on the mock. Value of these asserts is debatable.
    mockConn.execute.assert_called("""SELECT COUNT(*) from posts where post_id = ?""", (story.post_id,))
    mockConn.fetchone.assert_called()
Run Code Online (Sandbox Code Playgroud)