模仿全局变量

我一直在尝试为一个模块实现一些单元测试:

import database


def length_letters():
return len(letters)


def contains_letter(letter):
return letter in letters




letters = database.get('letters')   # returns a list of letters

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

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'))

我见过许多例子,其中“补丁”应用于方法和类,但不应用于变量。我不喜欢修补 Database.get方法,因为稍后我可能会用不同的参数再次使用它,所以我需要一个不同的响应。

我做错了什么?

124647 次浏览

您不需要使用 mock,只需导入模块并修改 setUp()中的 global 的值:

import alphabet


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

试试这个:

import unittests
import alphabet
from unittest import mock




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'))

您需要在单个测试实际运行时应用模拟,而不仅仅是在 setUp()中。我们可以在 setUp()中使用 创造模拟,然后使用 with ...上下文管理器应用它。

我遇到了一个问题,我试图模拟出在任何函数或类之外使用的变量,这是一个问题,因为在模拟值之前,在您尝试模拟类的时候,它们就被使用了。

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

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

os.environ["PROFILER_LOG_PATH"] = "./"

在我的课上:

log_path = os.environ.get("PROFILER_LOG_PATH",config.LOG_PATH)

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

变量可以修补如下:

from mock import patch
@patch('module.variable', new_value)

例如:

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')

如果您使用的是 pytest-mock(参见 https://pypi.org/project/pytest-mock/) ,那么您所需要做的就是使用内置的 fixture。

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...