模拟函数调用实际函数

mock function called actual function

我有 cassandra python 驱动程序的代码。

from cassandra.cqlengine.management import sync_table

def sync_my_tables():
    print sync_table
    print "*" * 80
    sync_table(my_models.student)

当我尝试为此编写 UT 时,我使用 @patch 嘲笑了 sync_table

from unittest import TestCase

from mock import patch


class TestCassandraSetup(TestCase):
    @patch('cassandra.cqlengine.management.sync_table', return_value=True)
    def test_sync_my_tables(self, _):
        from cassandra.cqlengine.management import sync_table
        print "*"*80
        print sync_table
        print "*"*80
        cass_setup.sync_my_tables()

patch后调用实际函数报错

Traceback (most recent call last):
  File "/venv/lib/python2.7/site-packages/mock/mock.py", line 1305, in patched
    return func(*args, **keywargs)
  File "/venv/tests/test_cassandra_setup.py", line 26, in test_sync_database_tables
    cassandra_client.sync_database_tables()
  File "/venv/code/cass_setup.py", line 18, in sync_my_tables
    sync_table(my_tables.student)
  File "/venv/lib/python2.7/site-packages/cassandra/cqlengine/management.py", line 200, in sync_table
    cluster = get_cluster()
  File "/venv/lib/python2.7/site-packages/cassandra/cqlengine/connection.py", line 182, in get_cluster
    raise CQLEngineException("%s.cluster is not configured. Call one of the setup or default functions first." % __name__)
CQLEngineException: cassandra.cqlengine.connection.cluster is not configured. Call one of the setup or default functions first.
-------------------- >> begin captured stdout << ---------------------
********************************************************************************
<MagicMock name='sync_table' id='4490003152'>
********************************************************************************
<function sync_table at 0x10b8075f0>
********************************************************************************

在 print 语句中,它第一次打印 MagicMock,但是当在实际代码中打印相同时,它打印实际函数而不是模拟对象。

为什么它在两者之间变化?

Python 不能模拟(替换)已经在模块范围内的东西。导入后,您无法从外部更改它。为了使代码可测试,您需要导入更高级别的模块并使用那里的方法,因为它只是一个可以更改的 属性。

from cassandra.cqlengine import management

def sync_my_tables():
    print management.sync_table
    print "*" * 80
    management.sync_table(my_models.student)

通过这种方式 mock 能够在运行时替换函数(management.sync_table = MagicMock())。


在您的测试函数中,您在函数被替换后执行导入,因此它按预期工作。

使用补丁时,需要对使用的对象进行补丁。因此,如果 sync_my_tables 位于路径为 foo/bar/baz.py 的文件中,您将需要像这样调用补丁:

@patch('foo.bar.baz.sync_table')
def test_sync_table(self, _):
    # test code