如何模拟python dict __str__方法?

时间:2018-04-24 20:45:26

标签: python unit-testing

我有一个方法,它需要包含某些条目的dict作为参数。例如,我可能期望dict中的type键的值必须是['typeA', 'typeB', 'typeC']之一。如果缺少某个键,我会使用该方法抛出异常SpecFormatException。异常消息应为'type' entry is missing in spec dict: {'foo': 'bar', 'baz': [1, 2, 3]}

我正在编写一个测试来验证是否正在使用正确的错误消息抛出异常,但由于dict.__str__()方法的输出是不确定的,因此我的测试很不稳定。我想如果我可以修补dict.__str__()方法输出类似"是的我就是你的字典"然后我可以修复片状,但是当我尝试用以下方法修补我的测试时:

import mock
import my_verifier

@mock.patch('my_verifier.dict')
def testErrorMessage(this, dict_str):
  dict_str.return_value = 'YES I AM YOUR DICTIONARY'
  ...

我收到错误消息" my_verifier.py没有属性' dict'"在试图进行测试时。

我假设嘲笑__str__方法是正确的方法,但我该如何做呢?

4 个答案:

答案 0 :(得分:1)

您对错误消息的测试需要具体吗?您是否可以断言您期望的部分内容,例如:

def function():
    global x
    x = x + 1
    if x <= 5: print(x)

显然,您可能还需要测试邮件的其他部分。对EXACT字符串的测试可能或不是您想要做的,因为该消息可能会更改。如果您需要沿着这条路走下去,我建议将字符串(或字符串模板)存储在可以被所有内容访问的地方,这样您的测试就可以根据类变量或配置参数进行断言。

答案 1 :(得分:0)

如果使用自定义dict2str函数替换内置dict.__str__方法是一个选项,则可以按字母顺序对键进行排序:

def det_str(x):
    if isinstance(x, dict):
        return '{{{}}}'.format(', '.join('{!r}: {}'.format(k, det_str(v)) for k, v in sorted(x.items())))
    else:
        return str(x)

>>> det_str({'a': 1, 'b': 0, 'c': -1})
"{'a': 1, 'b': 0, 'c': -1}"
>>> det_str({'c': -1, 'a': 1, 'b': 0})
"{'a': 1, 'b': 0, 'c': -1}"

也递归地工作:

>>> det_str({'b': 0, 'a': 1, 'c': -1, 'd': {'z': 10, 'x': 20, 'y': -30}})
"{'a': 1, 'b': 0, 'c': -1, 'd': {'x': 20, 'y': -30, 'z': 10}}"

答案 2 :(得分:0)

我会假设你在引发异常时将字典作为参数传递,例如

d = {'foo': 'bar', 'baz': [1, 2, 3]}
if not d.get('type') in ['typeA', 'typeB', 'typeC']:
    raise SpecFormatException(d)

第一个测试将仅关注异常本身,这意味着参数并不重要,只要它具有__str__方法:

def testExceptionMessage(self):
    d = mock.MagicMock()
    exc = SpecFormatException(d)
    observed = str(exc)
    self.assertEqual(observed,
                     "'type' entry is missing in spec dict: {}".format(d)

如果您在调用d.__str__.called之前和之后检查str(exc)的值,您会看到值从False更改为True,验证SpecFormatException确实使用参数的__str__方法来创建observed的值。

第二个测试侧重于按预期引发的异常。你真的不需要检查它的消息是什么,因为1)你已经在其他地方测试过了,2)消息实际上并不包含你无法从dict实例本身收集的任何信息事实上SpecFormatException确实被提出了。

def testRaiseException(self):
    with self.assertRaises(SpecFormatException):
        # code that should cause SpecFormatException be raised

答案 3 :(得分:0)

这是一个较旧的问题,但我遇到了同样的困难,无法找到有效的答案。我最终使用unittest.mock.MagicMock模拟对象函数的__str__输出。 MagicMockdesigned来模拟Python的魔术功能,例如__str__

对于测试本身,您不需要使用诸如SpecFormatException之类的东西。您可以使用mock.patch作为上下文管理器-您的代码如下所示:

import my_verifier
import unittest.mock


def test_my_method_error_message():
    with mock.patch("my_verifier.dict", mock.MagicMock()) as mock_dict:
        mock_dict.__str__.return_value = "YES I AM YOUR DICTIONARY"
        assert str(mock_dict) == "YES I AM YOUR DICTIONARY"

        # ...other tests that rely on a mocked instance of my_verifier.dict...

    # ...other tests that don't rely on a mocked instance of my_verifier.dict...

假设my_verifier有一个dict对象/方法,这使您可以控制一个或多个测试的打印my_verifier.dict的输出,而不必完全替换my_verifier.dict一个模拟对象。

您还可以在同一上下文中模拟my_verifier.dict的其他(魔术)函数,并使任何其他测试驻留在上下文之外。

patch.object方法也可能有用。