首页 > 解决方案 > Python 'call_command' mock 也用于其他测试

问题描述

使用 Django 1.10 和 python 3.5.1。

我正在尝试模拟“call_command”函数来引发异常。问题在于,它似乎在获得“side_effect”功能的那一刻 - 它也保留用于其他测试。我在做什么错或如何从该功能“恢复”副作用?

在此示例中,在运行其中一个测试之后,之后运行的所有其他测试将抛出相同的异常,即使它不应该在该测试中抛出异常。

 def test_run_migrations_raise_exception(self):

    with mock.patch('django.core.management.call_command', return_value=None, side_effect=Exception('e message')):
        self.check_migrations_called(MigrationTracker.objects.all(), data_migrations_settings_in_db)
        call_command('run_data_migrations')
        self.check_migrations_called(MigrationTracker.objects.all(), data_migrations_settings_in_db)

 def test_run_migrations_raise_flow_exception(self):

    with mock.patch('django.core.management.call_command', return_value=None, side_effect=FlowException(500, 'fe message', {'a': 1})):
        self.check_migrations_called(MigrationTracker.objects.all(), data_migrations_settings_in_db)
        call_command('run_data_migrations')
        self.check_migrations_called(MigrationTracker.objects.all(), data_migrations_settings_in_db)

标签: pythondjangounit-testing

解决方案


您不应该修补位于模块本地(即 Python 的“全局”-实际上是“模块”)命名空间中的函数。

当你在 Python 中时

from module.that import this

this成为包含导入语句的模块上的变量。对“module.that.this”的任何更改都会影响到另一个模块中指向的对象,但仅使用this仍会冷却到原始对象。

也许您的代码与您向我们展示的不完全一样,或者“mock.pacth”可能会在制作补丁时发现模块本地call_command指向django.core.management.call_command另一个模块 - 但在反转补丁时不会。事实是您的模块本地名称call_command正在更改。

您可以通过简单地更改代码以不将模块变量直接绑定到要更改的函数来解决此问题:

从 django.core 导入管理 def test_run_migrations_raise_exception(self):

with mock.patch('django.core.management.call_command', return_value=None, side_effect=Exception('e message')):
    self.check_migrations_called(MigrationTracker.objects.all(), data_migrations_settings_in_db)
    management.call_command('run_data_migrations')
    self.check_migrations_called(MigrationTracker.objects.all(), data_migrations_settings_in_db)

我希望你能理解并解决这个问题。现在,也就是说,这种使用mock完全没有意义:使用模拟的想法是,您在应用补丁的代码块中调用的代码间接使用的一些可调用对象没有原始效果 - 所以中间代码可以运行并进行测试。您正在直接调用模拟对象 - 所以它不会有任何原始代码 - 调用call_command('run_data_migrations')根本不会在您的代码库上运行任何代码,因此没有什么可以测试的。它只是调用模拟实例,它不会改变任何可以用check_migrations_called.


推荐阅读