模拟整个 class
Mocking an entire class
长话短说,我完全能够模拟 class 方法,当它只是被模拟对象替换的那个方法时,但是当我试图替换时我无法模拟那个方法整个 class 由模拟对象
@mock.patch.object
成功模拟了 scan
方法,但 @mock.patch
失败了。我已经按照这个例子
https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch
但显然我做错了什么。
我在这两种情况下都在同一个命名空间中模拟词典模块(它由 import lexicon
在 sentence_parser
中导入)但是 mock_lexicon is lexicon.lexicon
检查失败
#!python
import sys;
sys.path.append('D:\python\lexicon');
import lexicon;
import sentence_parser;
import unittest2 as unittest;
import mock;
class ParserTestCases(unittest.TestCase) :
def setUp(self) :
self.Parser = sentence_parser.Parser();
@mock.patch('lexicon.lexicon')
def test_categorizedWordsAreAssigned_v1(self, mock_lexicon) :
print "mock is lexicon:";
print mock_lexicon is lexicon.lexicon + "\n";
instance = mock_lexicon.return_value;
instance.scan.return_value = "anything";
self.Parser.categorize_words_in_sentence("sentence");
instance.scan.assert_called_once_with("sentence");
@mock.patch.object(lexicon.lexicon, 'scan')
def test_categorizedWordsAreAssigned_v2(self, mock_scan) :
mock_scan.return_value = "anything";
self.Parser.categorize_words_in_sentence("sentence");
mock_scan.assert_called_once_with("sentence");
if (__name__ == '__main__') :
unittest.main()
输出:
mock is lexicon:
False
======================================================================
FAIL: test_categorizedWordsAreAssigned_v1 (__main__.ParserTestCases)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\python\get_img\getImage_env\lib\site-packages\mock\mock.py", line 1305, in patched
return func(*args, **keywargs)
File "./test_sentence_parser.py", line 26, in test_categorizedWordsAreAssigned_v1
instance.scan.assert_called_once_with("sentence");
File "D:\python\get_img\getImage_env\lib\site-packages\mock\mock.py", line 947, in assert_called_once_with
raise AssertionError(msg)
AssertionError: Expected 'scan' to be called once. Called 0 times.
----------------------------------------------------------------------
Ran 2 tests in 0.009s
FAILED (failures=1)
编辑:
澄清一下,Parser
定义如下
#!python
import sys;
sys.path.append('D:\python\lexicon');
import lexicon;
class Parser(object) :
my_lexicon = lexicon.lexicon()
def __init__(self) :
self.categorized_words = ['test'];
def categorize_words_in_sentence(self, sentence) :
self.categorized_words = self.my_lexicon.scan(sentence);
if (__name__ == '__main__') :
instance = Parser();
instance.categorize_words_in_sentence("bear");
print instance.categorized_words;
这里真正相关的是categorize_words_in_sentence
Parser
的方法如何使用lexicon
。但首先我们应该去除噪音:
print mock_lexicon is lexicon.lexicon + "\n"
是什么会把我们引向错误的方向:尝试用
代替它
self.assertIs(mock_lexicon, lexicon.lexicon)
你会明白你正在打印 False
因为 mock_lexicon
不是 lexicon.lexicon + "\n"
而只是 lexicon.lexicon
.
现在我不能告诉你为什么第一个测试不起作用,因为答案在 categorize_words_in_sentence
方法中,或者更可能在 sentence_parser
模块中,我猜你可以有类似
from lexicon import lexicon
在这两种情况下,请查看 Where to Patch 文档,了解可能的原因以及您真正需要修补的地方。
第二个版本有效只是因为您修补的是对象而不是引用(应该不同)。
最后更简洁通用的版本可以是:
@mock.patch('lexicon.lexicon.scan', return_value="anything")
def test_categorizedWordsAreAssigned_v3(self, mock_scan) :
self.Parser.categorize_words_in_sentence("sentence")
mock_scan.assert_called_once_with("sentence")
还有一件事:删除 unittest2
至少你没有使用 python 2.4 并且你对向后移植的单元测试功能感兴趣。
[编辑]
现在我可以停止猜测并指出为什么第一个版本不起作用并且永远不会起作用:
class Parser(object) :
my_lexicon = lexicon.lexicon()
Parser.my_lexicon
属性在 加载时间 时计算。这意味着当您导入 sentence_parser
时,将创建一个 lexicon
并将引用关联到 Parser.my_lexicon
。当你修补 lexicon.lexicon
时,你保持这个引用不变,你的解析器对象仍然使用导入时创建的原始引用。
你可以做的是通过
修补Parser
class中的引用
@patch("sentence_parser.Parser.my_lexicon")
如果你想给你的 mock 相同的 lexicon
签名,你可以使用 create_autospect
。
@patch("sentence_parser.Parser.my_lexicon", create_autospec("lexicon.lexicon", instance=True))
长话短说,我完全能够模拟 class 方法,当它只是被模拟对象替换的那个方法时,但是当我试图替换时我无法模拟那个方法整个 class 由模拟对象
@mock.patch.object
成功模拟了 scan
方法,但 @mock.patch
失败了。我已经按照这个例子
https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch
但显然我做错了什么。
我在这两种情况下都在同一个命名空间中模拟词典模块(它由 import lexicon
在 sentence_parser
中导入)但是 mock_lexicon is lexicon.lexicon
检查失败
#!python
import sys;
sys.path.append('D:\python\lexicon');
import lexicon;
import sentence_parser;
import unittest2 as unittest;
import mock;
class ParserTestCases(unittest.TestCase) :
def setUp(self) :
self.Parser = sentence_parser.Parser();
@mock.patch('lexicon.lexicon')
def test_categorizedWordsAreAssigned_v1(self, mock_lexicon) :
print "mock is lexicon:";
print mock_lexicon is lexicon.lexicon + "\n";
instance = mock_lexicon.return_value;
instance.scan.return_value = "anything";
self.Parser.categorize_words_in_sentence("sentence");
instance.scan.assert_called_once_with("sentence");
@mock.patch.object(lexicon.lexicon, 'scan')
def test_categorizedWordsAreAssigned_v2(self, mock_scan) :
mock_scan.return_value = "anything";
self.Parser.categorize_words_in_sentence("sentence");
mock_scan.assert_called_once_with("sentence");
if (__name__ == '__main__') :
unittest.main()
输出:
mock is lexicon:
False
======================================================================
FAIL: test_categorizedWordsAreAssigned_v1 (__main__.ParserTestCases)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\python\get_img\getImage_env\lib\site-packages\mock\mock.py", line 1305, in patched
return func(*args, **keywargs)
File "./test_sentence_parser.py", line 26, in test_categorizedWordsAreAssigned_v1
instance.scan.assert_called_once_with("sentence");
File "D:\python\get_img\getImage_env\lib\site-packages\mock\mock.py", line 947, in assert_called_once_with
raise AssertionError(msg)
AssertionError: Expected 'scan' to be called once. Called 0 times.
----------------------------------------------------------------------
Ran 2 tests in 0.009s
FAILED (failures=1)
编辑:
澄清一下,Parser
定义如下
#!python
import sys;
sys.path.append('D:\python\lexicon');
import lexicon;
class Parser(object) :
my_lexicon = lexicon.lexicon()
def __init__(self) :
self.categorized_words = ['test'];
def categorize_words_in_sentence(self, sentence) :
self.categorized_words = self.my_lexicon.scan(sentence);
if (__name__ == '__main__') :
instance = Parser();
instance.categorize_words_in_sentence("bear");
print instance.categorized_words;
这里真正相关的是categorize_words_in_sentence
Parser
的方法如何使用lexicon
。但首先我们应该去除噪音:
print mock_lexicon is lexicon.lexicon + "\n"
是什么会把我们引向错误的方向:尝试用
代替它self.assertIs(mock_lexicon, lexicon.lexicon)
你会明白你正在打印 False
因为 mock_lexicon
不是 lexicon.lexicon + "\n"
而只是 lexicon.lexicon
.
现在我不能告诉你为什么第一个测试不起作用,因为答案在 categorize_words_in_sentence
方法中,或者更可能在 sentence_parser
模块中,我猜你可以有类似
from lexicon import lexicon
在这两种情况下,请查看 Where to Patch 文档,了解可能的原因以及您真正需要修补的地方。
第二个版本有效只是因为您修补的是对象而不是引用(应该不同)。
最后更简洁通用的版本可以是:
@mock.patch('lexicon.lexicon.scan', return_value="anything")
def test_categorizedWordsAreAssigned_v3(self, mock_scan) :
self.Parser.categorize_words_in_sentence("sentence")
mock_scan.assert_called_once_with("sentence")
还有一件事:删除 unittest2
至少你没有使用 python 2.4 并且你对向后移植的单元测试功能感兴趣。
[编辑]
现在我可以停止猜测并指出为什么第一个版本不起作用并且永远不会起作用:
class Parser(object) :
my_lexicon = lexicon.lexicon()
Parser.my_lexicon
属性在 加载时间 时计算。这意味着当您导入 sentence_parser
时,将创建一个 lexicon
并将引用关联到 Parser.my_lexicon
。当你修补 lexicon.lexicon
时,你保持这个引用不变,你的解析器对象仍然使用导入时创建的原始引用。
你可以做的是通过
修补Parser
class中的引用
@patch("sentence_parser.Parser.my_lexicon")
如果你想给你的 mock 相同的 lexicon
签名,你可以使用 create_autospect
。
@patch("sentence_parser.Parser.my_lexicon", create_autospec("lexicon.lexicon", instance=True))