本文介绍了模拟全班的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

长话短说,当类方法被模拟对象替换时,我完全能够模拟类方法,但是当我尝试用模拟代替整个类时,我无法模拟该方法对象

@mock.patch.object成功模拟了scan方法,但@mock.patch未能成功.我已经按照以下示例进行了操作 https://docs.python.org/3/library /unittest.mock.html#unittest.mock.patch 但显然我做错了.

在两种情况下,我都在相同名称空间中模拟lexicon模块(由import lexiconsentence_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))

这篇关于模拟全班的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-16 08:41