在Python中编写单元测试时,为什么后续测试中的mock不会覆盖之前测试中的mock?

问题描述 投票:0回答:1

我正在尝试编写单元测试,其中涉及为每个测试以不同的方式模拟多个库。当我单独运行每个测试时,它们都通过了,但是当我一起运行它们时,其中许多都失败了。它们失败的原因是,当在两个测试中模拟一个方法时,第二个测试中的模拟将被忽略,并且模拟函数始终按照第一个测试定义的方式运行。

我做了一个演示项目来演示这个问题:

文件结构

/.
├── 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)

我尝试使用装饰器进行修补,并手动启动和停止修补对象,如我的代码所示。

如何使我的单元测试尊重我对模拟函数所做的更新?

python unit-testing mocking python-unittest python-unittest.mock
1个回答
0
投票

您文件中的问题

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
© www.soinside.com 2019 - 2024. All rights reserved.