1-测试 - Mock数据

楔子

现在, 你要测试这些接口:

"""
用例集
case_set.py
pip install requests
"""
import requests

def v2ex_info():
    """
    获取v2ex的网站信息
    https://www.v2ex.com/api/site/info.json

    """
    response = requests.get(url='https://www.v2ex.com/api/site/info.json')
    return response.json().get('title')  # V2EX

def v2ex_stats():
    """
    获取v2ex的网站信息
    https://www.v2ex.com/api/site/stats.json
    """
    response = requests.get(url='https://www.v2ex.com/api/site/stats.json')
    return response.json().get('member_max')  # int类型

def cnodejs():
    """ 获取 cnodejs,推荐博客总数 """
    response = requests.get('https://cnodejs.org/api/v1/topics')
    return response.json().get('success')  # True

if __name__ == '__main__':
    print(v2ex_info() == 'V2EX')
    print(type(v2ex_stats()) is int)
    print(cnodejs() is True)

关于requests模块, see also:https://www.cnblogs.com/Neeo/articles/11511087.html

规则是:

  • v2ex_info接口返回值中的title是V2EX才算通过。
  • v2ex_stats接口返回值中的member_max是int类型才算通过。
  • cnodejs接口只值中的success是True算通过。

你根据规则很快的写出了测试用例:

"""
用例类
myMain.py
"""

import unittest
from case_set import v2ex_stats, v2ex_info, cnodejs

class InterfaceCase(unittest.TestCase):

    def test_v2ex_stats(self):
        """ 测试 v2ex_stats 接口,返回: int类型"""
        self.assertIs(type(v2ex_stats()), int)

    def test_v2ex_info(self):
        """ 测试 v2ex_info 接口, 返回: V2EX """
        self.assertEqual(v2ex_info(), 'V2EX')

    def test_cnodejs(self):
        """ 测试 cnodejs 接口,返回: True """
        self.assertIs(cnodejs(), True)

if __name__ == '__main__':
    unittest.main()

结果也OK:

M:\tests>python36 myMain.py -v
test_cnblogs_info (__main__.InterfaceCase)
测试博客园接口,返回: 200 ... ok
test_v2ex_info (__main__.InterfaceCase)
测试 v2ex_info 接口, 返回: V2EX ... ok
test_v2ex_stats (__main__.InterfaceCase)
测试 v2ex_stats 接口,返回: int类型 ... ok

----------------------------------------------------------------------
Ran 3 tests in 3.075s

OK

完美,你就准备去泡妞了......
为什么需要mock

桥逗麻袋!你在反复的执行测试用例时,发现test_cnodejs用例执行有些问题。有时候执行失败有时候成功,并且就算成功也响应时间较长,一番分析后,发现不是自己的问题,是接口暂时开发的不是很完善,导致现在测试不稳定。
但是你根据接口文档知道,这个接口这么测试,只要返回True就算通过。
那么能不能我们自己模拟出来这么一个接口,然后模拟一些方法和数据,在测试环境下使用。

什么是mock
在协同开发、测试中,总会出现各种问题,比如:

  • 开发人员某些接口还没有开发完毕。
  • 与第三方联调时,第三方拖了后腿,没准备好环境、数据都有可能。比如说我们测试的某个接口本身没有问题,但它依赖的某个接口有些问题,这就影响我们的正常测试任务进度。
  • 测试环境恶劣。
  • 开发只提供接口,数据自己搞!

这些问题总能影响我们的测试进度,那么我们怎么正常的展开呢?
这就需要mock来解决了。

什么是mock
mock是在测试过程中,对于一些不容易构造/获取的对象,创建一个mock对象来模拟对象的行为。
mock测试一般也称为mock数据。
简单来说,mock就是向测试对象提供一套和测试资源完全相同的接口和方法,不关系具体的实现过程,只关心具体结果。
mock测试的优点

  • 团队并行工作:有了mock,前后端人员只需要定义好接口文档就可以开始并行的工作,互不影响,只需要在最后联调的时候多多交流即可。后端与后端之间如果有接口耦合,也同样能被Mock解决;测试过程中如果遇到依赖接口没有准备好,同样可以借助Mock;不会出现一个团队等待另一个团队的情况。这样的话,开发自测阶段就可以及早开展,从而发现缺陷的时机也提前了,有利于整个产品质量以及进度的保证。
  • 开启TDD模式,即测试驱动开发:单元测试是TDD实现的基石,而TDD经常会碰到协同模块尚未开发完成的情况,但是有了mock,这些一切都不是问题。当接口定义好后,测试人员就可以创建一个Mock,把接口添加到自动化测试环境,提前创建测试。
  • 模拟出无法访问的资源:比如说,你需要调用一个“墙”外的资源来方便自己调试,就可以自己Mock一个。
  • 系统隔离:假如我们需要调用一个post请求,为了获得某个响应,来看当前系统是否能正确处理返回的“响应”,但是这个post请求会造成数据库中数据的污染,那么就可以充分利用Mock,构造一个虚拟的post请求,我们给他指定返回就好了。
  • 产品展示:假如我们需要创建一个演示程序,并且做了简单的UI,那么在完全没有开发后端服务的情况下,也可以进行演示。说到演示了,假如你已经做好了一个系统,并且需要给客户进行演示,但是里面有些真实数据并不想让用户看到,那么同样,你可以用Mock接口把这些敏感信息接口全部替换。
  • 测试覆盖:假如有一个接口,有100个不同类型的返回,我们需要测试它在不同返回下,系统是否能够正常响应,但是有些返回在正常情况下基本不会发生,难道你要千方百计地给系统做各种手脚让他返回以便测试吗?比如,我们需要测试在当接口发生500错误的时候,app是否崩溃,别告诉我你一定要给服务端代码做些手脚让他返回500 。。。而使用mock,这一切就都好办了,想要什么返回就模拟什么返回,妈妈再也不用担心你的测试覆盖度了。

关于TDD,see also:https://www.cnblogs.com/Neeo/articles/11504982.html
关于测试覆盖,see also:https://www.cnblogs.com/Neeo/articles/11795996.html

如何mock数据

下载安装

这里需要用到mock模块了,在Python3.x中,mock被集成到了unittest中,无需下载,直接导入即可,但在Python2.x中,就需要:

pip install mock

mock类的构成

这里以Python3.x为例。

快速上手

构造器:_init_

from unittest import mock

mock_obj = mock.Mock()
print(mock_obj)  # <Mock id='10069264'>
print(dir(mock_obj))
'''
[
    'assert_any_call', 'assert_called', 'assert_called_once', 
    'assert_called_once_with', 'assert_called_with', 'assert_has_calls', 
    'assert_not_called', 'attach_mock', 'call_args', 
    'call_args_list', 'call_count', 'called', 
    'configure_mock', 'method_calls', 'mock_add_spec', 
    'mock_calls', 'reset_mock', 'return_value', 
    'side_effect'
]
'''

虽然__init__是实例化方法,但在这里通常被称为构造器。
由打印结果可以看到,通过mock.Mock()实例化出一个mock对象mock_obj。这个对象是继承了Mock类的属性和方法。这样的一个mock对象对我们来说用处不大。
我们来试着添加一些自定义属性和方法,使之更灵活。
在Mock实例化时,我们可以传入这些参数:

  • name:mock对象的名字。它只是起到标识作用,当你print一个有name的mock对象时,可以看到它的name。
  • spec:mock对象的属性值。
  • side_effect:该参数指向一个可调用对象(一般是函数),当mock对象被调用时,如果该参数的返回值是默认的DEFAULT,则mock对象返回return_value指定的值,否则返回side_effect指定的对象的返回值。
  • return_value:该参数指定一个值或者对象,当mock对象被调用时,如果side_effect函数的返回值是DEFAULT,那么mock对象返回return_value指定的值或者对象。

注意,如果side_effect和return_value同时存在的时候,side_effect将会覆盖return_value。

name

from unittest import mock

mock_obj1 = mock.Mock()
mock_obj2 = mock.Mock(name='mock_obj2')
print(mock_obj1)  # <Mock id='50111760'>
print(mock_obj2)  # <Mock name='mock_obj' id='53781776'>

name参数没啥好说的,就是跟mock对象起了个名字。

为return_value指定某个值
现在让我们使用mock来模拟出文章开头的那几个接口测试中的cnodejs接口。

import unittest
from unittest import mock
from case_set import cnodejs  # 导入真实的cnodejs接口函数

class CnodejsTestCase(unittest.TestCase):

    def test_mock_cnodejs(self):
        """ 使用 mock 模拟的 cnodejs 接口 返回: True"""
        # 构造mock对象
        cnodejs = mock.Mock(return_value=True)
        # 使用mock对象进行断言
        self.assertIs(cnodejs(), True)

    def test_cnodejs(self):
        """ 测试 cnodejs 接口,返回: True """
        self.assertIs(cnodejs(), True)


if __name__ == '__main__':
    unittest.main()

用例test_mock_cnodejs方法中:
在Mock类实例化时传入return_value参数,然后构造出的mock对象赋值给cnodejs变量。然后cnodejs()相当于调用mock对象,得到返回值True,完事拿着这个返回值使用unittest进行断言。
用例test_cnodejs方法中,正常写测试用例断言,以判断两个用例方法有什么不同之处:

test_cnodejs (__main__.CnodejsTestCase)
测试 cnodejs 接口,返回: True ... ok
test_mock_cnodejs (__main__.CnodejsTestCase)
使用 mock 模拟的 cnodejs 接口 返回: True ... ok

----------------------------------------------------------------------
Ran 2 tests in 1.097s

OK

可以看到,两个用例方法都通过了,并没有什么区别。
在测试环境下,我们使用mock模拟的方法进行测试,这样能尽早的介入测试,带来的优势不一而足。
为return_value指定类的对象
return_value除了上述用法,还可以指定类的对象:

from unittest import mock


class Foo(object):
    """ 自定义类 """

    def f1(self):
        return 'this is Foo.f1'

    def f2(self, name):
        return name
# 正常的类的实例化与调用
foo_obj = Foo()
print(foo_obj.f1())  # this is Foo.f1
print(foo_obj.f2('this is Foo.f2'))  # this is Foo.f2

# 构造mock对象并传入 Foo实例化对象
foo_Class = mock.Mock(return_value=Foo())
# 想要得到mock对象的返回值,必须调用,也就是加括号
foo_obj = foo_Class()  # mock对象调用得到return_value值也就是Foo的实例化对象
# 接下里就是正常的调用了
print(foo_obj.f1())  # this is Foo.f1
# 同样可以正常传参
print(foo_obj.f2('this is Foo.f2'))  # this is Foo.f2

使用mock对象模拟类的实例化对象同样方便。

side_effect
先来看第一个示例,我们可以为mock对象的side_effect参数指定可迭代对象。

from unittest import mock

mock_obj1 = mock.Mock(return_value=100)
print(mock_obj1())  # 100
mock_obj2 = mock.Mock(return_value=100, side_effect=[200, 300])
print(mock_obj2())  # 200

由上例可以看到,如果在构造mock对象的时候,只有return_value被指定,调用mock对象返回return_value指定的值。
side_effectreturn_value同时被指定时,side_effect就覆盖了return_value
那么既然side_effect接受的是一个可迭代对象,我们就可以多次调用它:

from unittest import mock

mock_obj1 = mock.Mock(return_value=100)
print(mock_obj1())  # 100
mock_obj2 = mock.Mock(return_value=100, side_effect=[200, 300])
print(mock_obj2())  # 200
print(mock_obj2())  # 300
print(mock_obj2())  # StopIteration

可以看到side_effect对象本质上是一个生成器。

为spec指定属性组成的列表
现在我们使用mock来模拟出来两V2EX的两个接口方法。

import unittest
from unittest import mock
from case_set import v2ex_info, v2ex_stats

# 为mock对象的spec参数传入属性(方法)组成的列表
spec_list = ['v2ex_info', 'v2ex_stats']
mock_obj = mock.Mock(spec=spec_list)
print(spec_list)  # ['v2ex_info', 'v2ex_stats']
# 根据真实的接口规则设置两个方法的返回值
mock_obj.v2ex_info.return_value = 'V2EX'
mock_obj.v2ex_stats.return_value = 466668  # 该接口只需要返回值是int即可

class TestCaseDemo(unittest.TestCase):

    def test_v2ex_stats(self):
        """ 测试 v2ex_stats 接口,返回: int类型"""
        self.assertIs(type(v2ex_stats()), int)

    def test_mock_v2ex_stats(self):
        """ mock v2ex_stats 接口,返回: int类型 """
        v2ex_stats = mock_obj.v2ex_stats
        self.assertIs(type(v2ex_stats()), int)

    def test_mock_v2ex_info(self):
        """ mock v2ex_info 接口, 返回: V2EX """
        v2ex_info = mock_obj.v2ex_info
        self.assertEqual(v2ex_info(), 'V2EX')

    def test_v2ex_info(self):
        """ 测试 v2ex_info 接口, 返回: V2EX """
        self.assertEqual(v2ex_info(), 'V2EX')

if __name__ == '__main__':
    unittest.main()

结果:

M:\tests>python36 myMain.py -v
test_mock_v2ex_info (__main__.TestCaseDemo)
mock v2ex_info 接口, 返回: V2EX ... ok
test_mock_v2ex_stats (__main__.TestCaseDemo)
mock v2ex_stats 接口,返回: int类型 ... ok
test_v2ex_info (__main__.TestCaseDemo)
测试 v2ex_info 接口, 返回: V2EX ... ok
test_v2ex_stats (__main__.TestCaseDemo)
测试 v2ex_stats 接口,返回: int类型 ... ok

----------------------------------------------------------------------
Ran 4 tests in 3.154s

OK

由结果发现,我们用mock模拟的两个接口都通过了。
为spec指定类属性

from unittest.mock import Mock
class Foo(object):
    age = 20
    def f1(self):
        return 'this if f1'

    def f2(self, name):
        return name
mock_obj = Mock(spec=Foo)
print(mock_obj.f1)  # <Mock name='mock.f1' id='1847131683640'>
print(mock_obj.f2)  # <Mock name='mock.f2' id='1847131615128'>
print(mock_obj.age)  # <Mock name='mock.age' id='1847131718880'>
print(mock_obj.name)  # AttributeError: Mock object has no attribute 'name'

我们为mock对象指定了属性为Foo类,那么,类中的方法和属性都是mock对象的属性,这也是前三个打印没有问题的原因,而第4个打印报错了,显然,Foo类中没有一个叫name的属性或者方法。

mock断言语句

由mock思维导图我们知道,mock关于断言有这些常用的:

  • assert_called_with(arg):检查函数调用参数是否正确。
  • assert_called_once_with(arg):检查函数调用参数是否正确,但是只调用一次。
  • assert_any_call():用于检查测试的mock对象在测试例程中是否调用了方法。
  • assert_has_calls():期望调用方法列表。

assert_called_with
assert_called_with检查mock方法是否获取了正确的参数,当至少有一个参数有错误的值或者类型时、当参数的个数出错时、当参数的顺序不正确时,断言失败。

from unittest.mock import Mock

class Foo(object):
    value = 20

    def f1(self, arg):
        return arg

    def f2(self, *args):
        return args

mock_obj = Mock(spec=Foo)
# f1正确的传参姿势
mock_obj.f1(222)
# mock_obj.f1.assert_called_with()  # 报错,没有传参
# mock_obj.f1.assert_called_with(11)  # 报错,瞎98传参
# mock_obj.f1.assert_called_with('6669')  # 报错,6翻了吧,传值的类型不对
# mock_obj.f1.assert_called_with(222)  # 噢啦,mock_obj.f1()传的就是 222

# f2正确传参姿势
mock_obj.f2(1, 2, 3)
# mock_obj.f2.assert_called_with() # 报错,没有传参
# mock_obj.f2.assert_called_with(1)  # 报错,少传了参数
# mock_obj.f2.assert_called_with(1, 3, 2)  # 报错,传参顺序不对
mock_obj.f2.assert_called_with(1, 2, 3)  # 噢啦,传参姿势很对

啥都不用说了吧,都在代码里。

assert_called_once_with
assert_called_once_with断言,当指定方法被多次调用的时候,断言失败。

from unittest.mock import Mock

class Foo(object):
    value = 20

    def f1(self, arg):
        return arg

    def f2(self, *args):
        return args
# 实例化mock对象
mock_obj = Mock(spec=Foo)
# 为f1方法赋返回值
mock_obj.f1.return_value = 222
print(mock_obj.f1())
mock_obj.f1.assert_called_once_with()  # 第一次调用,没问题
print(mock_obj.f1())
mock_obj.f1.assert_called_once_with()  # 第二次调用,报错 AssertionError: Expected 'f1' to be called once. Called 2 times.

这个断言相对简单。

assert_any_call
assert_any_call断言用于检查测试执行中的mock对象在测试中是否调用了方法。

from unittest.mock import Mock

class Foo(object):
    value = 20

    def f1(self, arg):
        return arg

    def f2(self, *args):
        return args


mock_obj = Mock(spec=Foo)
# mock对象调用了 f1() f1(100) f1(200) f1(200)
mock_obj.f1()
mock_obj.f1(100)
mock_obj.f1(200)
mock_obj.f1(200)

# 判断:mock对象调用了f1() f1(100) f1(200) f1(300) f2()
mock_obj.f1.assert_any_call()  # 没错
mock_obj.f1.assert_any_call(100)  # 没错
mock_obj.f1.assert_any_call(200)  # 没错
# mock_obj.f1.assert_any_call(300)  # AssertionError: f1(300) call not found
mock_obj.f2.assert_any_call()  # AssertionError: f2() call not found

上例,assert_any_call会判断整个测试中方法是否被调用了。而不管该方法是否被重复调用。
例如,在程序执行时执行了mock_obj.f1.assert_any_call(),那么就用mock_obj.f1.assert_any_call()判断刚才的方法是否执行过。执行过啥都不做,要是没执行过就报错。

assert_has_calls
assert_has_calls检查是否按照正确的顺序和正确的参数进行调用的。所以,我们需要给出一个方法的调用顺序,assert的时候按照这个顺序进行检查。

from unittest.mock import Mock
from unittest.mock import call  # 引入新的模块

class Foo(object):
    value = 20

    def f1(self, arg):
        return arg

mock_obj = Mock(spec=Foo)
# 正确的执行顺序是 f1() f1(100) f1(200)
mock_obj.f1()
mock_obj.f1(100)
mock_obj.f1(200)

# 报错, 现在的执行顺序是 f1() f1(100) f1(300)
# calls_list = [call.f1(), call.f1(100), call.f1(300)]  # 报错,没有 call.f1(300)
# mock_obj.assert_has_calls(calls_list)

# 报错,现在的执行顺序是 f1(200) f1() f1(300)
# calls_list = [call.f1(200), call.f1(), call.f1(200)]  # 报错,执行顺序不对
# mock_obj.assert_has_calls(calls_list)

# 对喽
calls_list = [call.f1(), call.f1(100), call.f1(200)]
mock_obj.assert_has_calls(calls_list)

首先,我们以列表的形式列出方法调用顺序,每个方法前使用call.f1()的形式,因为如果不加call来修饰的话, 解释器将不知道f1是一个方法,当然call在使用之前需要引入。

mock管理方法

mock中,关于管理有这些常用方法:

  • attach_mock:将一个mock对象添加到另一个mock对象中。
  • configure_mock,更改mock对象的return_value值。
  • mock_add_spec:给mock对象添加新的属性。
  • reset_mock:将mock对象恢复到初始状态。

acttach_mock
acttach_mock将一个mock对象添加到另一个mock对象中。

from unittest.mock import Mock


class Foo(object):

    def f1(self, arg):
        return arg


class Bar(object):

    def f2(self, *args):
        pass

# 分别构造foo和bar的mock对象
mock_foo = Mock(spec=Foo)
mock_bar = Mock(spec=Bar)

# 打印也没问题
print(mock_foo, mock_bar)  # <Mock spec='Foo' id='57738096'> <Mock spec='Bar' id='130627728'>

# 分别为两个mock对象的方法添加返回值
mock_foo.f1.return_value = 'Foo.f1'
mock_bar.f2.return_value = 'Bar.f2'

# 正常的调用都没问题
print(mock_foo.f1())  # Foo.f1
print(mock_bar.f2())  # Bar.f2

# 使用attach_mock将mock_bar对象添加到mock_foo中
mock_foo.attach_mock(mock_bar, 'bar')

# 现在mock_bar对象成为了mock_foo mock对象的一个属性bar
print(mock_foo.bar)  # <Mock name='mock.bar' spec='Bar' id='132987120'>

# mock_foo.bar等于拿到了mock_bar对象,然后调用其中的f2方法,并且得到了之前赋值的返回值
print(mock_foo.bar.f2())  # Bar.f2

需要注意的是,attach_mock(self, mock, attribute)必须为添加进来的mock对象指定一个属性名。

configure_mock

configure_mock用来更改mock对象的return_value值。

from unittest.mock import Mock

class Foo(object):

    def f1(self, arg):
        return arg

    def f2(self, arg):
        return arg

# 实例化mock对象并添加属性和返回值
mock_obj = Mock(spec=Foo, return_value='abc')
# 正常调用mock对象得到预期的结果 abc
print(mock_obj())  # abc
# 使用configure_mock修改mock对象的return_value值
mock_obj.configure_mock(return_value='xyz')
# 修改成功
print(mock_obj())  # xyz

# 可以批量设置返回值,比如f1方法的返回值为 '100', f2方法的返回值为 200
spec_dict = {'f1.return_value': '100', 'f2.return_value': 200}
# 将字典打散后使用configure_mock设置到mock对象中
mock_obj.configure_mock(**spec_dict)
print(mock_obj())  # xyz
print(mock_obj.f1())  # 100  ps:字符串类型的100
print(mock_obj.f2())  # 2090

mock_add_spec
mock_add_spec(self, spec, spec_set=False)用来给mock对象添加一个新的属性,新的属性会覆盖掉原来的属性。spec_set指属性可读可写,默认是只读,但可写我没测试出来....欢迎留言指正。

from unittest.mock import Mock

class Foo(object):

    def f1(self, arg):
        return arg

class Bar(object):

    def f2(self, args):
        return args

def ace():
    pass

# 实例化mock对象
mock_obj = Mock(spec=Foo)
print(mock_obj.f1())  # <Mock name='mock.f1()' id='119946576'>

# 使用mock_add_spec给mock_obj添加一个新的属性
mock_obj.mock_add_spec(Bar)
print(mock_obj.f2())  # <Mock name='mock.f2()' id='46952912'>

# 正常的使用都没问题
mock_obj.f2.return_value = 'Bar.f2'
print(mock_obj.f2())  # Bar.f2

# 上面添加的属性是类,现在是函数,记得函数这里没有方法,别瞎点啊
mock_obj.mock_add_spec(ace)
mock_obj.return_value = 'function'
print(mock_obj())  # function

# 另外,新添加的属性会覆盖掉之前的属性。现在的mock对象模拟的函数ace对象,ace函数哪有什么f1啊
print(mock_obj.f1())  # AttributeError: Mock object has no attribute 'f1'

reset_mock
reset_mock将mock对象回复到初识状态,避免了重新构造mock对象带来的开销。

from unittest.mock import Mock

class Foo(object):

    def f1(self, arg):
        return arg

mock_obj = Mock(spec=Foo)
mock_obj.f1()
# 这里如果不使用 reset_mock, 那么f1方法就被调用了两次,下面的 assert_called_once_with就会报错,现在则不报错了
mock_obj.reset_mock()
mock_obj.f1()
mock_obj.f1.assert_called_once_with()

mock统计方法

再来看mock关于统计的一些方法:

  • called:跟踪mock对象所做的任意调用的访问器。
  • mock_calls:显示工厂调用和方法调用。
  • call_args:mock对象的初始化参数。
  • call_args_list:调用中使用参数。
  • call_count:mock对象被调用次数。
  • method_calls:以列表的形式返回mock对象都调用了哪些方法。

called

from unittest.mock import Mock

def ace():
    pass

# 构造 mock对象并没有调用
mock_obj = Mock(spec=ace)
# OK,此时mock对象没有调用,所以mock_obj.called:False
print(mock_obj.called)  # False
# OK,现在调用了,那么 mock_obj.called:True
mock_obj()
print(mock_obj.called)  # True

called只要检测到mock对象被调用,就返回True。

call_count

from unittest.mock import Mock

def ace():
    pass

mock_obj = Mock(spec=ace)
mock_obj()
mock_obj()
mock_obj()
print(mock_obj.call_count)  # 3

call_count检查mock对象被调用了多少次。

call_args && call_args_list

from unittest.mock import Mock

mock_obj = Mock()
mock_obj()
print(mock_obj.call_args)  # call()
print(mock_obj.call_args_list)  # [call()]

call_args_list以列表的形式返回工厂调用时所有的参数。

method_calls

from unittest.mock import Mock

class Foo(object):

    def f1(self, arg):
        return arg

mock_obj = Mock(spec=Foo)
mock_obj()
mock_obj.f1()
print(mock_obj.call_args)  # call()
print(mock_obj.call_args_list)  # [call()]
print(mock_obj.method_calls)  # [call.f1()]

mock_calls

from unittest.mock import Mock

class Foo(object):

    def f1(self, arg):
        return arg

mock_obj = Mock(spec=Foo)
mock_obj()
mock_obj.f1()
print(mock_obj.mock_calls)  # [call(), call.f1()]

首先,mock对象被调用时,执行工厂call方法,完事第二次调用了f1方法,所以mock_calls返回了两个方法。


欢迎斧正,that's all see also:[Mock测试概念介绍](https://www.jianshu.com/p/3944c0b82f30) | [如何 mock 数据](https://www.jianshu.com/p/63056120fab8) | [Python中的模块学习之mock模块](https://blog.csdn.net/peiyao456/article/details/77075173)
posted @ 2019-09-12 11:55  听雨危楼  阅读(1858)  评论(0编辑  收藏  举报