【问题标题】:How to provide conditional arguments to a mock function in Python?如何在 Python 中为模拟函数提供条件参数?
【发布时间】:2013-04-03 05:59:45
【问题描述】:

我的项目使用 Python 的 urllib2.urlopen 对外部 API 进行各种调用。我正在使用 NoseTests 进行单元测试,并使用 MiniMock 来模拟对 urllib2.urlopen 的调用。

模拟代码:

from hashlib import md5
from os.path import dirname, join
from urllib2 import Request, urlopen

from minimock import mock, restore

def urlopen_stub(url, data=None, timeout=30):
    """
    Mock urllib2.urlopen and return a local file handle or create file if
    not existent and then return it.
    """

    if isinstance(url, Request):
        key = md5(url.get_full_url()).hexdigest()
    else:
        key = md5(url).hexdigest()
    data_file = join(dirname(__file__), 'cache', '%s.xml' % key)
    try:
        f = open(data_file)
    except IOError:
        restore() # restore normal function
        data = urlopen(url, data).read()
        mock('urlopen', returns_func=urlopen_stub, tracker=None) # re-mock it.
        with open(data_file, 'w') as f:
            f.write(data)
        f = open(data_file, 'r')
    return f

mock('urlopen', returns_func=urlopen_stub, tracker=None)

我正在像这样运行我的测试:

from os.path import isfile, join
from shutil import copytree, rmtree

from nose.tools import assert_raises, assert_true

import urlopenmock

class TestMain(object):
    working = 'testing/working'

    def setUp(self):
        files = 'testing/files'
        copytree(files, self.working)

    def tearDown(self):
        rmtree(self.working)

    def test_foo(self):
        func_a_calling_urlopen()
        assert_true(isfile(join(self.working, 'file_b.txt')))

    def test_bar(self):
        func_b_calling_urlopen()
        assert_true(isfile(join(self.working, 'file_b.txt')))

    def test_bar_exception(self):
        assert_raises(AnException, func_c_calling_urlopen)

最初我在一个单独的模块中检查异常,该模块导入了一个不同的模拟文件,该文件在调用urlopen 时返回了一个损坏的 XML 文件。但是,导入该模拟类会覆盖上面显示的类,因为每次都使用损坏的 XML,所以会破坏所有测试。

我认为这是因为异常测试模块是在其他模块之后加载的,因此它的导入是最后调用的,并且返回损坏的 XML 的模拟函数覆盖了原始模拟函数。

我希望能够告诉模拟代码在运行 test_bar_exception 时使用损坏的 XML 文件,以便引发异常。我该怎么做呢?

【问题讨论】:

  • 对于 Python 的出色模拟系统,请查看Fudge

标签: python unit-testing mocking nose


【解决方案1】:

在我看来,您需要在每个需要使用它的测试上设置和拆除您的模拟 urlopen,并使用不同的模拟 urlopen 来为那些处理该错误条件的测试返回一个损坏的 xml 文件。比如:

class TestMain(object):
    # ...

    def test_foo(self):
        mock('urlopen', returns_func=urlopen_stub, tracker=None)
        func_a_calling_urlopen()
        assert_true(isfile(join(self.working, 'file_b.txt')))
        restore()

    # ...

    def test_bar_exception(self):
        mock('urlopen', 
                returns_func=urlopen_stub_which_returns_broken_xml_file, 
                tracker=None)
        assert_raises(AnException, func_c_calling_urlopen)
        restore()

但是,如果测试引发异常并且未到达 restore() 调用,则上述内容存在问题。您可以使用try: ... finally: 进行包装,但是,对于每个测试来说,这是很多繁文缛节。

您可能想看看 Michael Foord 的 mock 库。

Pycon 演示文稿:http://blip.tv/file/4881513

看看patch,它为您提供了装饰器和上下文管理器选项,用于替换和恢复您在 urlopen 上的调用。它非常漂亮!

【讨论】:

    【解决方案2】:

    假设你的请求输入是'a url',输出是'aresponse',输入'burl'输出是'bresponse',所以使用

    @mock.patch('requests.get', mock.Mock(side_effect = lambda k:{'aurl': 'a response', 'burl' : 'b response'}.get(k, 'unhandled request %s'%k)))
    

    【讨论】:

      猜你喜欢
      • 2018-11-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多