模拟python类的任何实例上的方法

Jam*_*ong 29 python testing mocking

我想在生产代码中模拟某些类的任何实例上的方法,以便于测试.Python中有没有可以促进这个的库?

基本上,我想要执行以下操作,但在Python中(以下代码是Ruby,使用Mocha库):

  def test_stubbing_an_instance_method_on_all_instances_of_a_class
    Product.any_instance.stubs(:name).returns('stubbed_name')
    assert_equal 'stubbed_name', SomeClassThatUsesProduct.get_new_product_name
  end
Run Code Online (Sandbox Code Playgroud)

从上面要注意的重要一点是我需要在类级别上模拟它,因为我实际上需要在我正在测试的事物创建的实例上模拟方法.

使用案例:

我有一个类QueryMaker在一个实例上调用一个方法RemoteAPI.我想模拟出RemoteAPI.get_data_from_remote_server返回一些常量的方法.如何在测试中执行此操作,而无需在RemoteAPI代码中放置特殊情况以检查其运行的环境.

我想要的实例:

# a.py
class A(object):
    def foo(self):
        return "A's foo"

# b.py
from a import A

class B(object):
    def bar(self):
        x = A()
        return x.foo()

# test.py
from a import A
from b import B

def new_foo(self):
    return "New foo"

A.foo = new_foo

y = B()
if y.bar() == "New foo":
    print "Success!"
Run Code Online (Sandbox Code Playgroud)

fuz*_*man 52

在测试时需要模拟方法很常见,并且有很多工具可以帮助你在Python中使用它.像这样的"猴子修补"类的危险在于,如果你之后没有撤消它,那么在整个测试过程中,类已被修改用于所有其他用途.

我的库mock是最受欢迎的Python模拟库之一,它包含一个名为"patch"的帮助程序,可帮助您在测试期间安全地修补对象和类的方法或属性.

模拟模块可从以下位置获得:

http://pypi.python.org/pypi/mock

补丁装饰器可以用作上下文管理器或测试装饰器.您既可以使用它自己修补功能,也可以使用它自动修补可配置的Mock对象.

from a import A
from b import B

from mock import patch

def new_foo(self):
    return "New foo"

with patch.object(A, 'foo', new_foo):
    y = B()
    if y.bar() == "New foo":
        print "Success!"
Run Code Online (Sandbox Code Playgroud)

这会自动处理取消修补.你可以在没有自己定义模拟函数的情况下逃脱:

from mock import patch

with patch.object(A, 'foo') as mock_foo:
    mock_foo.return_value = "New Foo"

    y = B()
    if y.bar() == "New foo":
        print "Success!"
Run Code Online (Sandbox Code Playgroud)

  • 这是我遇到的最令人困惑的代码示例.B == A和foo == bar是没有办法的.然而,这两个在这个例子中可以互换使用. (5认同)
  • 需要知道的重要一点是,如果您按名称进行修补,则在模块中修改名称**,而不是在定义的模块中.例如,如果我想在模块`foo`中修补`SomeClass`的使用,即使`someClass`在模块`bar`中定义但导入到`foo`,那么你修补的是`foo.SomeClass`和*不是*`bar.SomeClass`.对于修补类方法而言,类*object*在两个模块中都是同一个对象并不重要.对于其他事情,它非常重要. (3认同)

Fus*_*ush 7

模拟就是这样做的方法,好吧。确保在从类创建的任何实例上修补实例方法可能有点棘手。

# a.py
class A(object):
    def foo(self):
        return "A's foo"

# b.py
from a import A

class B(object):
    def bar(self):
        x = A()
        return x.foo()

# test.py
from a import A
from b import B
import mock

mocked_a_class = mock.Mock()
mocked_a_instance = mocked_a_class.return_value
mocked_a_instance.foo.return_value = 'New foo'

with mock.patch('b.A', mocked_a_class):  # Note b.A not a.A
    y = B()
    if y.bar() == "New foo":
        print "Success!"
Run Code Online (Sandbox Code Playgroud)

在文档中引用,在以“配置修补类上的实例方法的返回值...”开头的段落中


kin*_*all 4

最简单的方法可能是使用类方法。您确实应该使用实例方法,但创建这些方法很麻烦,而有一个内置函数可以创建类方法。使用类方法,您的存根将获得对类(而不是实例)的引用作为第一个参数,但由于它是一个存根,这可能并不重要。所以:

Product.name = classmethod(lambda cls: "stubbed_name")
Run Code Online (Sandbox Code Playgroud)

请注意,lambda 的签名必须与您要替换的方法的签名匹配。当然,由于 Python(如 Ruby)是一种动态语言,因此无法保证在您掌握实例之前有人不会将您的存根方法切换为其他语言,尽管我希望您很快就会知道如果发生这种情况。

编辑:在进一步调查中,您可以省略classmethod()

Product.name = lambda self: "stubbed_name"
Run Code Online (Sandbox Code Playgroud)

我试图尽可能地保留原始方法的行为,但看起来实际上没有必要(并且无论如何都没有像我希望的那样保留行为)。