问题描述
长话短说,当类方法被模拟对象替换时,我完全能够模拟类方法,但是当我尝试用模拟代替整个类时,我无法模拟该方法对象
@mock.patch.object
成功模拟了scan
方法,但@mock.patch
未能成功.我已经按照以下示例进行了操作 https://docs.python.org/3/library /unittest.mock.html#unittest.mock.patch 但显然我做错了.
在两种情况下,我都在相同名称空间中模拟lexicon模块(由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
在这两种情况下,请查看在何处进行修补文档,可以启发您找出可能的原因以及在案例中真正需要修补的内容.
第二个版本的工作仅是因为您正在修补对象而不是引用(应该有所不同).
最后,更为简洁和通用的版本可以是:
@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,并且对反向移植的unittest功能感兴趣.
现在,我可以停止猜测并指出为什么第一个版本不起作用并且永远不起作用:
class Parser(object) :
my_lexicon = lexicon.lexicon()
Parser.my_lexicon
属性是在加载时间进行评估的.这意味着当您导入sentence_parser
时,将创建一个lexicon
并将引用关联到Parser.my_lexicon
.修补lexicon.lexicon
时,将保留该引用不变,并且解析器对象仍将使用导入时创建的原始引用.
您可以做的是通过
@patch("sentence_parser.Parser.my_lexicon")
修补Parser
类中的引用
@patch("sentence_parser.Parser.my_lexicon")
如果您可以使用 create_autospect
想要给您的模拟对象相同的lexicon
签名.
@patch("sentence_parser.Parser.my_lexicon", create_autospec("lexicon.lexicon", instance=True))
Long story short, I'm perfectly able to mock class method, when it's just that method that's replaced by mock object, but I'm unable to mock that method when I'm trying to replace the whole class by the mock object
The @mock.patch.object
successfully mocks the scan
method but @mock.patch
fails to do so. I've followed the example at https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patchbut apparently I'm doing something wrong.
I'm mocking the lexicon module in the same namespace in both cases (it's imported by import lexicon
in the sentence_parser
) but the mock_lexicon is lexicon.lexicon
check fails
#!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()
Output :
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)
EDIT :
To clarify, the Parser
is defined as follows
#!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;
What is real relevant here is how categorize_words_in_sentence
Parser
's method use lexicon
. But first of all we should remove the noise:
print mock_lexicon is lexicon.lexicon + "\n"
Is what can lead us to the wrong direction: try to replace it by
self.assertIs(mock_lexicon, lexicon.lexicon)
and you will understand that you are printing False
because mock_lexicon
is not lexicon.lexicon + "\n"
but just lexicon.lexicon
.
Now I cannot tell you why the first test doesn't work because the answer is in categorize_words_in_sentence
method or more probably in sentence_parser
module where I can guess you can have something like
from lexicon import lexicon
In both case take a look to Where to Patch documentation that can enlighten you on what can be the cause and what you really need to patch in your case.
The second version works just because you are patching the object and not the reference (that should be different).
Finally the more concise and general version can be:
@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")
One more thing: remove unittest2
at least you're not using python 2.4 and you are interested on backported unittest features.
[EDIT]
Now I can stop to guess and point to you why the first version doesn't work and will never work:
class Parser(object) :
my_lexicon = lexicon.lexicon()
Parser.my_lexicon
attribute is evaluated at the load time. That means when you import sentence_parser
a lexicon
is created and the reference associated to Parser.my_lexicon
. When you patch lexicon.lexicon
you leave this reference untouched and your parser object still use the original reference created when is imported.
What you can do is to patch the reference in Parser
class by
@patch("sentence_parser.Parser.my_lexicon")
You can use create_autospect
if you want give to your mock the same lexicon
's signature.
@patch("sentence_parser.Parser.my_lexicon", create_autospec("lexicon.lexicon", instance=True))
这篇关于模拟全班的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!