繁体   English   中英

避免在单元测试中运行顶级模块代码

[英]Avoiding running top-level module code in unit test

我正在尝试对一些导入模块的 Python 3 代码进行单元测试。 不幸的是,模块的编写方式,简单地导入它会产生令人不快的副作用,这对测试并不重要。 我正在尝试使用unitest.mock.patch来解决它,但运气不佳。

这是一个说明性示例的结构:

.
└── work
    ├── __init__.py
    ├── test_work.py
    ├── work.py
    └── work_caller.py

__init__.py是一个空文件

工作.py

import os


def work_on():
    path = os.getcwd()
    print(f"Working on {path}")
    return path

def unpleasant_side_effect():
    print("I am an unpleasant side effect of importing this module")

# Note that this is called simply by importing this file
unpleasant_side_effect()

work_caller.py

from work.work import work_on

class WorkCaller:
    def call_work(self):
        # Do important stuff that I want to test here

        # This call I don't care about in the test, but it needs to be called
        work_on()

测试工作.py

from unittest import TestCase, mock

from work.work_caller import WorkCaller


class TestWorkMockingModule(TestCase):
    def test_workcaller(self):
        with mock.patch("work.work.unpleasant_side_effect") as mocked_function:
            sut = WorkCaller()
            sut.call_work()

work_caller.py ,我只想测试开始代码,而不是对work_on()的调用。 当我运行测试时,我得到以下 output:

paul-> python -m unittest
I am an unpleasant side effect of importing this module
Working on /Users/paul/src/patch-test
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

我原以为I am an unpleasant side effect of importing this module行不会被打印,因为 function unpleasant_side_effect会被嘲笑。 我可能哪里出错了?

unpleasant_side_effect的运行有两个原因。 首先,因为导入是在测试用例开始之前处理的,因此在导入时不会被模拟。 其次,因为 mocking 本身导入了work.py ,因此即使没有导入work_caller.py也会运行unpleasant_side_effect

导入问题可以通过 mocking 模块work.py本身来解决。 这可以在测试模块或测试用例本身中全局完成。 在这里,我为它分配了一个MagicMock ,可以导入、调用等。

测试工作.py

from unittest import TestCase, mock


class TestWorkMockingModule(TestCase):
    def test_workcaller(self):
        import sys
        sys.modules['work.work'] = mock.MagicMock()
        from work.work_caller import WorkCaller

        sut = WorkCaller()
        sut.call_work()

缺点是 work_on 也被嘲笑,我不确定你的情况是否有问题。

导入时不可能不运行整个模块,因为函数和类也是语句,因此模块执行必须在返回到调用者之前完成,调用者想要更改导入的模块。

如果您部分询问最佳实践。

您应该始终将代码拆分为所有其他代码和副作用行使用的库。 并且可能通过调用def main():但如果你想保留副作用,那么你可以这样做:

work_lib.py

...no side-effects...

work.py

from work_lib import ...

...side-effects....

test_work.py

from work_lib import ...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM