unittest.mock是用于在单元测试中模拟和替换指定的对象及行为,以便测试用例更加准确地进行测试运行。例如对于以下代码,想要针对函数func_a写一个简单的单元测试:
但是这样的话,函数func_b和func_c的逻辑都需要一起测试,在单元测试中这明显是不合理的,对于想要测试的函数func_a,里面所使用到的其他函数或接口,我们只需要关心它的返回值即可,保证当前测试的函数按它自己的逻辑运行,所以可以写成下面这样:
注意,模拟的mock_func_b并不需要保证func_a中所有的可能分支和逻辑都执行一次,单元测试更多的是验证函数或接口(比如这里的func_a)是否与设计相符、发现代码实现与需求中存在的错误、修改代码时是否引入了新的错误等。但是这里的写法也有很大的问题,一个功能模块中使用的函数或接口通常来讲其实并不少、也没有这里这么简单,如果涉及的接口都要重新写一个mock对象(如mock_func_b),那单元测试的工作将会变得非常繁重和复杂,所以unittest中的mock模块派上了用场,这个模块也正如它的名称一样,可以模拟各种对象。
mock模块中的Mock类最常用的就是Mock和MagicMock,可以用来模拟对象、属性和方法,并且会保存这些被模拟的对象的使用细节,之后再使用断言来判断它们是否按照期待的被使用。
使用Mock类指定其被调用时触发的一些行为(Mock对象也可以用于替换指定的对象或方法)。
使用spec参数指定Mock对象的属性和方法,指定时可以是一个对象,会自动将该对象的属性和方法赋给当前Mock对象,但是注意赋值的属性和方法也是Mock类型的,并不会真正执行对应方法的内容。
使用MagicMock创建并替换原有的方法。
Mock类虽然支持对Python中所有的magic方法进行“mock”,并允许给magic方法赋予其他的函数或者Mock实例,但是如果需要使用到magic方法,最简单的方式是使用MagicMock类,它继承自Mock并实现了所有常用的magic方法。
可以使用create_autospec函数来创建所有和原对象一样的api。
Mock对象可以用来模拟对象、属性和方法,Mock对象也会记录自身被使用的过程,你可以通过相关assert方法来测试验证代码是否被执行过。MagicMock类是Mock类的一个子类,它实现了所有常用的magic方法。
构造函数 参数解释:
- spec: 可以传入一个字符串列表、类或者实例,如果传入的是类或者实例对象,那么将会使用 方法将该类或实例转化为一个字符串列表(magic属性和方法除外)。访问(get操作)任何不在此列表中的属性和方法时都会抛出AttributeError。如果传入的是一个类或者实例对象,那么__class__方法会返回对应的类,以便在使用 方法时进行判断。
- spec_set: spec参数的变体,但更加严格,如果试图使用get操作或set操作来操作此参数指定的对象中没有的属性或方法,则会抛出AttributeError。spec参数是可以对spec指定对象中没有的属性进行set操作的。参考 方法。
- side_effect: 可以传入一个函数,每次当Mock对象被调用的时候,就会自动调用该函数,可以用于抛出异常或者动态改变mock对象的返回值,此函数使用的参数与mock对象被调用时传入的参数是一样的,并且,除非它的返回值为 对象,否则这个函数的返回值将会作为mock对象的返回值。也可以传入一个exception对象或者实例对象,如果传入exception对象,则每次调用mock对象都会抛出该异常。也可以传入一个可迭代对象,每次调用mock对象时就会返回该迭代对象的下一个值。如果不想使用了,可以将它设置为None。具体参见后面mock对象 属性的使用。
- return_value: 每次调用mock对象时的返回值,默认第一次调用时创建新的Mock对象。
- unsafe: 如果某个属性或方法中会assert一个AttributeError,则可以设置 来跳过这个异常。(Python3.5更新)
- wraps: 包裹Mock对象的对象,当wraps不为None时,会将Mock对象的调用传入wraps对象中,并且可以通过Mock对象访问wraps对象中的属性。但是如果Mock对象指定了明确的return_value那么wraps对象就不会起作用了。
- name: 指定mock对象的名称,可在debug的时候使用,并且可以“传播”到子类中。
- 注: 初始化Mock对象时,还可以传入其他任意的关键字参数,这些参数会被用于设置成Mock对象的属性,具体参见后面的 。
assert_called()
assert:mock对象至少被调用过一次。(Python3.6新增)
assert_called_once()
assert:mock对象只被调用过一次。(Python3.6新增)
assert_called_with(*args, kwargs)
assert:mock对象最后一次被调用的方式。
assert_called_once_with(*args, kwargs)
assert:mock对象以指定方式只被调用过一次。
assert_any_call(*args, kwargs)
assert:mock对象以指定方式被调用过。
assert_has_calls(calls, any_order=False)
calls是一个 对象列表,any_order默认为False,表示calls中的对象必须按照原来的调用顺序传入,为True则表示可以是任意顺序。
assert:mock对象以calls中指定的调用方式被调用过。
assert_not_called()
assert:mock对象没有被调用过。(Python3.5新增)
reset_mock(*, return_value=False, side_effect=False)
mock_add_spec(spec, spec_set=False)
spec参数可以是一个对象或者一个字符串列表,如果指定了此参数,那么只有spec指定的属性才可以进行访问(get操作)。如果spec_set设置为True,那么只有spec中指定的属性才可以进行set操作。
attach_mock(mock, attribute)
将一个mock对象作为一个子属性添加到当前mock对象,并且会将其name值和parent关系进行替换。注意,此方法的调用会被记录在 方法和 方法中。
configure_mock(kwargs)
添加额外的属性到已经创建的mock对象,并且可以给属性添加return_value值和side_effect值。在创建mock对象时也可以用这种方式添加额外的属性。
called
如果mock对象被调用过则返回True,否则返回False。
call_count
返回mock对象被调用的次数。
return_value
指定mock对象被调用时的返回值,也可以在创建mock对象时通过参数进行指定。如果没有进行指定,return_value的默认值为一个mock对象,而且它就是一个正常的mock对象,你可以把它当成普通的mock对象进行其他操作。
side_effect
这个属性可以是函数、可迭代对象或者异常(类或实例都可以),当mock对象被调用时, 属性对应的对象就会被调用一次。
如果传入的是函数,那么它将在mock对象调用时被执行,且执行时此函数传入的参数与mock对象被调用时的参数是一致的,此函数的返回值即mock被对象调用的返回值,但是如果函数的返回值是 对象,那么mock对象被调用的返回值就是它自身的return_value属性值。
如果传入的是一个可迭代对象,那么这个对象将被用作产生一个迭代器,这个迭代器在每一次mock对象被调用时返回一个值,这个值可以是异常类的实例,也可以是一个普通的值,当然如果这个返回值是一个 对象,则返回mock对象本身的return_value属性值。
是一个异常:
是一个可迭代对象:
是一个 :
创建mock对象时指定 为一个函数:
将 指定为None,即可清除该选项:
call_args
call_args_list
存储mock对象调用的列表,列表元素为call对象,在没有被调用之前为空列表。
method_calls
存储mock对象调用以及“调用的调用“的列表,列表元素为call对象,在没有被调用之前为空列表。
mock_calls
存储mock对象所有类型调用的列表。
class
如果mock对象指定了spec对象,则会返回spec对象的类型,也可以直接赋值。这个属性主要是在 进行判断的时候会用到。
这是一个不可被调用的mock类,它的参数和Mock类的使用是一样的,不过 和 这两个参数对 类来说是无意义的。
这是一个专门用于替换属性的Mock类,它提供了属性对应的get和set方法。
一个MagicMock的异步版本,AsyncMock对象会像一个异步函数一样运行,它的调用的返回值是一个awaitable对象,这个awaitable对象返回 或者 指定的值。
如果Mock或者MagicMock的spec参数指定了一个异步的函数,那么对应mock对象的调用将返回一个协程对象。
如果Mock、MagicMock或者AsyncMock的spec参数指定了带有同步或者异步函数的类,那么对于Mock,所有的同步函数将被定义为Mock对象,对于MagicMock和AsyncMock,所有同步函数将被定义为MagicMock。而对于Mock、MagicMock或者AsyncMock,所有的异步函数都将被定义为AsyncMock对象。
assert_awaited()
assert:mock对象至少被await过一次。注意,await的对象是被从mock对象中分离出来的,且该分离出来的对象必须被await关键字声明过才能进行assert判断。
assert_awaited_once()
assert:mock对象只被await了一次。
assert_awaited_with(*args, kwargs)
assert:mock对象最后一次的await的参数和指定的参数一致。
assert_awaited_once_with(*args, kwargs)
assert:mock对象只被await过一次,且使用的参数和指定的参数一致。
assert_any_await(*args, kwargs)
assert:mock对象以指定的参数await过。
assert_has_awaits(calls, any_order=False)
assert:mock对象以指定的call对象的调用方式await过。any_order用于指定是否需要判断call调用的顺序,默认需要判断。
assert_not_awaited()
assert:mock对象没有被await过。
reset_mock(*args, kwargs)
与 使用相似,会将 置为0, 置为None,清除 中的内容。
await_count
mock对象被await的次数。
await_args
mock对象最近一次被await的调用信息,是一个call对象。如果没有被await过,则为None。和 相似。
await_args_list
是一个记录mock对象所有的await调用信息的列表,列表元素为call对象,初始值为空列表。
Mock对象每次调用都会返回 属性,默认的 是一个新的Mock对象,它会在第一次 被访问时创建,并且以后每次访问 都会返回第一次创建的Mock对象。
Mock的每次调用都会记录在 和 中。具体使用示例见之前的2.2.2章节。
如果设置了 属性,那么在调用时,会先记录此次调用信息,再去调用 指定的对象。所以想要mock对象的调用抛出一个异常的最简单方式就是使用 属性指定一个异常类或者异常实例。
如果 是一个函数,那么调用mock对象的时候就会使用相同的参数去调用此函数。
如果想要mock对象的调用返回一个默认值,那么可以有以下两种方式:在 指定的函数中直接返回 ,或者返回 对象。
如果想要移除 并返回mock的默认值,将它设置为None就可以了。
的值也可以是可迭代对象,每次调用会依次获取可迭代对象中的下一个值,一直到可迭代对象的末尾,并触发StopIteration异常。如果可迭代对象中含有异常,当迭代到此异常时将会抛出该异常。
如果想要设置mock对象的name属性,可以有两种方式:使用 ,或者直接给mock对象赋值 。
如果mock对象的属性是另一个mock对象时,这个属性的mock就相当于是父mock的子mock,子mock的调用会被记录在父mock的 和 中,如果你不想子mock的调用被记录,则可以在定义子mock时指定name属性,指定了name属性的子mock则不会被记录在父mock中。
如果需要将一个含有name属性的子mock对象赋给父mock,且可以记录子mock的调用,则需要使用attach_mock方法来将子mock赋给父mock。
可以用装饰器的方式对属性、方法和类进行装饰,或者在with上下文中使用,或者使用start和stop方法直接在代码中使用。使用patch的目的是在代码运行时将指定的对象变为执行mock对象,并且是在单元测试开始时就可以指定所有的mock对象,非常方便。
可以使用patch装饰器替换某个模块的类,但是注意,导入时需要使用import导入对应的模块,也只能到模块这一级,函数中传参的顺序也必须是与装饰的顺序一致(从下到上)。
可以使用with语法来使用 装饰器。
可以会使用 替换原有的字典对象。
可以作为一个函数装饰器,类装饰器,或者上下文管理器(with语句)。
构造函数 参数解释:
- target:target参数是一个形如的字符串。target值将会被import并创建一个新的对象,所以target字符串必须是在当前环境可以import的。需要注意,被装饰的函数执行时,target的对象才会被创建,而不是运行装饰器的时候被创建。
- new:如果没有指定,则对于async函数会创建一个AsyncMock对象,对于其他的,则会创建一个MagicMock对象。如果 是作为一个装饰器,且new参数没有指定,则创建的mock对象将会作为一个额外(即放在被装饰函数原有的参数之后)的参数传入被装饰的函数。如果 用在上下文管理器中,则创建的mock对象会被上下文管理器返回。
- spec和spec_set:会当作参数传入MagicMock中。如果创建的是spec或spec_set对象,可以设置spec=True或者spec_set=True,以便让patch正常运行。
- new_callable:可以是一个类或者一个callable对象,并会使用此参数创建一个对象,默认情况下,对于async函数会创建一个AsyncMock对象,对于其他的,则会创建一个MagicMock对象。
- create:默认为False,如果指定为True,那么当patch的对象或函数不存在时会自动创建,当真正的对象在运行过程中被程序创建后就删除patch出来的mock对象,这个参数特别适用于一些运行时创建的内容。(Python3.5更新:如果想要patch的内容是 内建模块,则不用指定 ,patch会在运行时自动创建。)
patch可以作为一个装饰器为函数创建一个mock对象并传入被装饰的函数。如果patch装饰的是一个类,那么将会返回一个MagicMock对象,当这个类在test方法中被实例化时,那么将会返回此MagicMock对象的 值,注意,如果在一个test方法中实例化多次,也是返回的同一个对象,如果想要每次都返回新的不同的对象,那么可以使用 参数。
如果mock了一个类,对该类的实例对象和真实的class进行 判断,则需要指定 。
patch默认创建的是MagicMock对象,如果想要创建一个指定的对象,就可以使用 参数。甚至可以使用 参数在test case中重定向输出。
patch中可以通过传参的方式给mock对象设置属性。
可以通过字典的方式来配置mock对象的属性。
patch.object用来给对象(target参数)的成员(attribute参数)进行“mock”,其参数的用法和patch是一样的,且也可以使用参数的形式给创建的mock对象添加额外的属性。如果被装饰的对象是类的话,可以使用 指定哪些方法需要被“mock”。
patch.object被用来装饰一个函数的时候,那么被创建的mock对象会一个额外参数的形式传入被装饰的函数。
用来“mock”一个字典对象或者类似字典的对象,参数为需要“mock”的字典对象,也可以是一个可以通过import生成字典对象的字符串,values参数为创建的字典对象的内容,也可以是(key, value)形式的键值对。当test case结束后,原先的被mock的字典对象就会恢复。
可以使用参数配置的方式给字典对象添加内容。
也支持一些类似字典但不是字典类型的对象,但是这些对象必须具有以下Magic方法: , , ,以及 和 中的一个。
可以一次性创建多个mock对象,参数的用法和patch是一样的。
使用patch.multiple创建多个mock对象时,需要使用 对象。
也可以和patch作为装饰器一起使用,但是 产生的额外参数传入被装饰的函数时需要放在patch的参数后面。
如果 在with中使用,则with返回的是一个字典对象。
如果不想使用装饰器或with语法而直接使用patch,那么可以使用patch的start方法和stop方法。start方法能直接返回对应的mock对象,而stop方法则是取消使用patch,类似with语句的开始和结束。
使用start和stop方法的另一个典型例子是test case的setUp和tearDown方法。
调用了start后一定要记得调用stop,也可以在最后使用stopall方法一次性stop所有使用了start方法的patch对象。如果怕自己在最后忘记了调用stop方法,也可以在调用了start方法后,立即调用 方法,此方法会在最后自动调用stop。
注: 此学习笔记大多是直接从官方文档翻译过来的https://docs.python.org/3/library/unittest.mock.html
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.mushiming.com/mjsbk/7108.html