【问题标题】:Is it possible to prevent a module/global class variable from creating instance for unit test?是否可以防止模块/全局类变量为单元测试创​​建实例?
【发布时间】:2020-10-02 22:00:29
【问题描述】:

我需要为模块编写测试用例

to_be_tested.py

from module_x import X

_x = X() # creating X instance in test environment will raise error

#.....

在测试用例中,

from unittest import TestCase, mock

class Test1(TestCase):

    @mock.patch('...to_be_tested._x')
    @mock.patch('...to_be_tested.X.func1')
    def test_1(self, mock_func1, mock_x):
        ...

但是,这不会阻止import 创建实例。这是一种解决方法并为模块编写测试用例吗?或者它是一种将to_be_tested 重构为可测试的方法?

如果检测到测试环境,也许写to_be_tested.py,只写_x = None

【问题讨论】:

  • 如果to_be_tested.py 在您的控制之下,修改它不要在导入时创建实例,而是将其延迟到第一次使用。如果您无法控制to_be_tested.py,请在此处查看解决方案:Mocking a module import in pytest
  • 是的,我现在可以完全控制源代码。我将把变量封装在一个函数中:_x = None / def get_x(): global _x / if _x == None: _x = X() / return _x。然后其他函数使用该函数访问_x。这是个好方法吗?

标签: python unit-testing mocking


【解决方案1】:

X 在全局级别的实例化似乎有问题,但我没有完整的情况,所以我不能明确地说“不要那样做”。如果您可以重构它,以便根据需要或类似的方式创建 X() 实例,那将是理想的。

也就是说,这是一种防止module_x 在测试期间被导入的方法。我的假设是 X() 在整个 module_x 模块中使用,因此该模块中实际上不需要任何东西,您只想模拟它。

import sys
import unittest

from unittest import TestCase, mock

class Test1(TestCase):

    @classmethod
    def setUpClass(cls):
        sys.modules['module_x'] = mock.Mock()

    @mock.patch('to_be_tested._x')
    @mock.patch('to_be_tested.X.func1')
    def test_1(self, mock_func1, mock_x):
        from to_be_tested import _x
        print(_x)

您可以看到 _x 现在是一个模拟,但请注意,您不能在测试之外进行导入(就像大多数导入一样,在测试模块的顶部),因为 sys.modules['module_x'] 没有'还没有被替换掉。

【讨论】:

    【解决方案2】:

    一种可能性是使用环境变量保护_x 的创建,以便您可以在测试模式下禁用其初始化。例如,

    import os
    from module import X
    
    _x = None
    if 'TEST' not in os.environ:
        _x = X()
    

    现在,您只需确保在导入 to_be_tested 之前在您的环境中设置了 TEST。您可能会在测试运行程序中执行此操作,但也可以直接在您的测试模块中执行。

    from unittest import TestCase, mock
    import os
    
    
    os.environ['TEST'] = ''
    
    import to_be_tested
    
    class Test1(TestCase):
        ...
    

    【讨论】:

    • 您的意思可能是if 'TEST' not in os.environ:。顺便说一句,这是一个非常丑陋的解决方案,并且模块可能已经在测试发现阶段以一种或另一种方式导入,因此从测试模块中设置 os.environ 可能为时已晚。
    • 测试发现通常是通过文件名或从特定文件的目录完成的,尽管与任何约定一样,可能存在使这种方法脆弱的例外情况。
    • X 实例化为需要显式调用的模块级函数会更简洁,但会强制 production 用户调用测试的函数代码似乎没有倒退。
    • 说到底,这更多的是设计问题,而不是测试中需要解决的问题。
    • 如果您像@chepner 建议的那样在生产代码中使用函数调用实例化X,您可以在测试中模拟该函数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-29
    • 2015-05-04
    • 1970-01-01
    • 1970-01-01
    • 2014-01-22
    • 1970-01-01
    相关资源
    最近更新 更多