2

我想测试一个方法,无论它是否调用临时内部对象的特定方法。(ConfigParser.read)

所以对象是在内部创建的,方法退出后就不能从外部访问了。

使用 python 2.7

在 foobar.py

 import ConfigParser

 class FooBar:
     def method(self, filename):
         config=ConfigParser.ConfigParser()
         config.read(filename)
         do_some_stuff()

我想测试是否调用了 config.read 。

据我了解,补丁装饰器是为此而制作的,但不幸的是,测试用例接收到的 MagicMock 对象与内部创建的对象不同,我无法靠近方法内部的对象。

我试过这样:

class TestFooBar(TestCase):

    def setUp(self):
         self.myfoobar = FooBar()

    @mock.patch('foobar.ConfigParser')
    def test_read(self,mock_foobar):
        self.myfoobar.method("configuration.ini")
        assert mock_foobar.called # THIS IS OKAY
        assert mock_foobar.read.called # THIS FAILS
        mock_foobar.read.assert_called_with("configuration.ini") # FAILS TOO

问题是: - mock_foobar 是在 self.myfoobar.method 在内部创建 ConfigReader 之前创建的。- 调试 mock_foobar 时有关于先前调用的内部数据,但没有“读取”属性(用于模拟读取方法的内部 MagicMock)

当然,一种出路是重构并给 .read() 或init () 一个 ConfigReader 对象,但并不总是可以更改代码,我想在不接触模块的情况下掌握方法的内部对象正在测试中。

4

1 回答 1

4

你这么近!问题是您正在模拟该类,但随后您的测试检查该模拟类是否调用了 read() - 但您实际上希望在调用该类时返回的实例上调用 read()。以下工作 - 我发现第二个测试比第一个更具可读性,但它们都有效:

import ConfigParser
from unittest import TestCase

from mock import create_autospec, patch, Mock 


class FooBar(object):
    def method(self, filename):
        config=ConfigParser.ConfigParser()
        config.read(filename)


class TestFooBar(TestCase):

    def setUp(self):
         self.myfoobar = FooBar()

    @patch('ConfigParser.ConfigParser')
    def test_method(self, config_parser_class_mock):
        config_parser_mock = config_parser_class_mock.return_value

        self.myfoobar.method("configuration.ini")

        config_parser_class_mock.assert_called_once_with()
        config_parser_mock.read.assert_called_once_with("configuration.ini")

    def test_method_better(self):
        config_parser_mock = create_autospec(ConfigParser.ConfigParser, instance=True)
        config_parser_class_mock = Mock(return_value=config_parser_mock)

        with patch('ConfigParser.ConfigParser', config_parser_class_mock):
            self.myfoobar.method("configuration.ini")

        config_parser_class_mock.assert_called_once_with()
        config_parser_mock.read.assert_called_once_with("configuration.ini")
于 2015-08-26T23:14:06.427 回答