模拟一个全局变量

Fun*_*tic 20 python unit-testing patch mocking

我一直在尝试为模块实现一些单元测试.名为alphabet.py的示例模​​块如下:

import database

def length_letters():
    return len(letters)

def contains_letter(letter):
    return True if letter in letters else False


letters = database.get('letters')   # returns a list of letters
Run Code Online (Sandbox Code Playgroud)

我想用我选择的一些值来模拟数据库的响应,但下面的代码似乎不起作用.

import unittests  
import alphabet   
from unittest.mock import patch   


class TestAlphabet(unittest.TestCase): 
    @patch('alphabet.letters')
    def setUp(self, mock_letters):
        mock_letters.return_value = ['a', 'b', 'c']   

    def test_length_letters(self):
        self.assertEqual(3, alphabet.length_letters())

    def test_contains_letter(self):   
        self.assertTrue(alphabet.contains_letter('a'))
Run Code Online (Sandbox Code Playgroud)

我见过很多例子,其中'patch'应用于方法和类,但不适用于变量.我不想修补方法database.get,因为我可能会在以后使用不同的参数再次使用它,所以我需要一个不同的响应.

我在这做错了什么?

Wil*_*ill 27

试试这个:

import unittests  
import alphabet   
from unittest.mock import patch   


class TestAlphabet(unittest.TestCase): 
    def setUp(self):
        self.mock_letters = mock.patch.object(
            alphabet, 'letters', return_value=['a', 'b', 'c']
        )

    def test_length_letters(self):
        with self.mock_letters:
            self.assertEqual(3, alphabet.length_letters())

    def test_contains_letter(self):
        with self.mock_letters:
            self.assertTrue(alphabet.contains_letter('a'))
Run Code Online (Sandbox Code Playgroud)

您需要在单个测试实际运行时应用模拟,而不仅仅是setUp().我们可以创建模拟setUp(),稍后使用with ...Context Manager 应用它.

  • 使用`return_value`将导致字母成为可调用的MagicMock.但我们并没有将字母称为函数,并且我们不需要MagicMock的任何属性,我们只想替换该值.所以我们应该直接传递这个值:`mock.patch.object(alphabet,'letters',['a','b','c'])` (5认同)

Val*_*iuk 25

变量可以修补(Python2)

from mock import patch
@patch('module.variable', new_value)    
Run Code Online (Sandbox Code Playgroud)

例如:

import alphabet
from mock import patch
@patch('alphabet.letters', ['a', 'b', 'c'])
class TestAlphabet():

    def test_length_letters(self):
        assert 3 == alphabet.length_letters()

    def test_contains_letter(self):
       assert alphabet.contains_letter('a')
Run Code Online (Sandbox Code Playgroud)

我认为也应该适用于Python3,因为mockunittest.mock的backport

  • 该解决方案有效且干净。也可以只修补测试类中的一些测试 (2认同)

Ali*_*jad 12

如果您正在使用pytest-mock(请参阅https://pypi.org/project/pytest-mock/),那么您需要做的就是使用内置的固定装置。

def test_my_function(mocker):
    # Mock the value of global variable `MY_NUMBER` as 10
    mocker.patch("path.to.file.MY_NUMBER", return_value=10)
    # rest of test...
Run Code Online (Sandbox Code Playgroud)


小智 5

我遇到了一个问题,我试图模拟在任何函数或类之外使用的变量,这是有问题的,因为它们是在您尝试模拟类时使用的,然后才能模拟值。

我最终使用了环境变量。如果环境变量存在,则使用该值,否则使用应用程序默认值。这样我就可以在测试中设置环境变量值。

在我的测试中,在导入类之前我有这段代码

os.environ["PROFILER_LOG_PATH"] = "./"
Run Code Online (Sandbox Code Playgroud)

在我的课堂上:

log_path = os.environ.get("PROFILER_LOG_PATH",config.LOG_PATH)
Run Code Online (Sandbox Code Playgroud)

默认情况下我的config.LOG_PATH/var/log/<my app name>,但是现在当测试运行时,日志路径设置为当前目录。这样您就不需要 root 访问权限来运行测试。