我正在尝试编写单元测试,其中涉及为每个测试以不同的方式模拟多个库。当我单独运行每个测试时,它们都通过了,但是当我一起运行它们时,其中许多都失败了。它们失败的原因是,当在两个测试中模拟一个方法时,第二个测试中的模拟将被忽略,并且模拟函数始终按照第一个测试定义的方式运行。
我做了一个演示项目来演示这个问题:
/.
├── src
│ └── main.py
└── test
├── __init__.py
├── test_base.py
└── test.py
main.py
import boto3
s3 = boto3.client('s3')
def main():
return s3.list_buckets()
__init__.py
from .test import *
test_base.py
import unittest
from unittest.mock import MagicMock, patch
class TestBase(unittest.TestCase):
def setUp(self):
self.patch_boto3_client = patch('boto3.client', autospec=True)
self.mock_boto3_client = self.patch_boto3_client.start()
# BOTO3
# Set up mock boto3 clients
self.mock_s3 = MagicMock()
# Configure mock_boto3_client to return our mock clients
self.mock_boto3_client.side_effect = lambda service: {
's3': self.mock_s3,
}[service]
def tearDown(self) -> None:
self.patch_boto3_client.stop()
测试.py
from .test_base import TestBase
class Test(TestBase):
def test_1(self):
from src.main import main
self.mock_s3.list_buckets.return_value = 'test_1'
response = main()
self.assertEqual(response, 'test_1')
def test_2(self):
from src.main import main
self.mock_s3.list_buckets.return_value = 'test_2'
response = main()
self.assertEqual(response, 'test_2')
$ python3 -m coverage run -m unittest test
.F
======================================================================
FAIL: test_2 (test.test.Test.test_2)
----------------------------------------------------------------------
Traceback (most recent call last):
File ".../test/test.py", line 21, in test_2
self.assertEqual(response, 'test_2')
AssertionError: 'test_1' != 'test_2'
- test_1
? ^
+ test_2
? ^
----------------------------------------------------------------------
Ran 2 tests in 0.292s
FAILED (failures=1)
我尝试使用装饰器进行修补,并手动启动和停止修补对象,如我的代码所示。
如何使我的单元测试尊重我对模拟函数所做的更新?
您文件中的问题
test.py
是在第二种测试方法(test_2()
)中导入指令:
from src.main import main
不会导致指令的执行:
s3 = boto3.client('s3')
存在于
main.py
中,因此 s3
中的 main.py
仍然等于第一个测试中创建的 Mock 对象。
相反,通过
test_1()
执行指令 s3 = boto3.client('s3')
作为导入指令 from src.main import main
的效果,并为 s3
分配由方法 mock_s3
创建的 Mock 对象 setUp()
。
第二个测试可以修改如下(评论突出显示更改):
from .test_base import TestBase
from unittest import mock # <--- ADD this import
class Test(TestBase):
def test_1(self):
...
def test_2(self):
from src.main import main, s3 # <--- ADD HERE THE IMPORT of s3
# remove your return_value
#self.mock_s3.list_buckets.return_value = 'test_2'
# ADD the following with instruction
with mock.patch('src.main.s3') as mock_s3:
mock_s3.list_buckets.return_value = 'test_2'
response = main()
self.assertEqual(response, 'test_2')
测试的执行给出以下输出:
$ python3 -m coverage run -m unittest test
..
----------------------------------------------------------------------
Ran 2 tests in 0.131s
OK