前言

mock的介绍

py3已将mock集成到unittest库中;
为的就是更好的进行单元测试
简单理解,模拟接口返回参数;
通俗易懂,直接修改接口返回参数的值;

mock的作用

1、解决依赖问题,达到解耦作用
当我们测试某个目标接口(模块)时,该接口依赖其他接口,当被依赖的接口未开发完成时,可以用mock模拟被依赖接口,完成目标接口的测试;

2、模拟复杂业务的接口
当我们测试某个目标接口(模块),该接口依赖一个非常复杂的接口时,可以用mock来模拟这个复杂的业务接口;也解决接口依赖一样的原理

3、单元测试
如果某个接口(模块)未开发完成时,又需要编写测试用例,则可以通过mock模拟该接口(模块)进行测试

4、前后端联调
前端开发的页面需要根据后端返回的不同状态码展示不同的页面,当后端接口未开发完成时,也可通过mock来模拟后端接口返回自己想要的数据

mock类解读

  1. class Mock(spec=None,side_effect=None,return_value=DEFFAULT,name=None)

secp:定义mock对象的属性值,可以是列表,字符串,甚至一个对象或者实例
side_effect:可以用来抛出异常或者动态改变返回值,它必须是一个iterator(列表),它会覆盖return_value
return_value:定义mock方法的返回值,它可以是一个值,可以是一个对象(如果存在side_effect参数那这个就没有用,也就是不能同时用)
name:作为mock对象的一个标识,在print时可以看到

mock实际使用

一个未开发完成的功能如何测试?


def add(self, a, b):

"""两个数相加"""

pass



class TestSub(unittest.TestCase):

"""测试两个数相加用例"""


def test_sub(self):

# 创建一个mock对象 return_value代表mock一个数据

mock_add = mock.Mock(return_value=15)

# 将mock对象赋予给被测函数

add = mock_add

# 调用被测函数

result = add(5, 5)

# 断言实际结果和预期结果

self.assertEqual(result, 15)

一个完成开发的功能如何测试?


class SubClass(object):

def add(self, a, b):

"""两个数相加"""

return a + b



class TestSub(unittest.TestCase):

"""测试两个数相加用例"""



def test_add2(self):

# 初始化被测函数类实例

sub = SubClass()

# 创建一个mock对象 return_value代表mock一个数据

# 传递side_effect关键字参数, 会覆盖return_value参数值, 使用真实的add方法测试

sub.add = Mock(return_value=15, side_effect=sub.add)

# 调用被测函数

result = sub.add(5, 5)

# 断言实际结果和预期结果

self.assertEqual(result, 10)

side_effect:这里给的参数值是sub.add相当于add方法的地址,当我们调用add方法时就会调用真实的add方法

简单理解成:传递了side_effect参数且值为被测函数地址时,mock不会起作用;两者不可共存

另外,side_effect接受的是一个可迭代序列,当传递多个值时,每次调用mock时会返回不同的值;


mock_obj = mock.Mock(side_effect= [1,2,3])

print(mock_obj())

print(mock_obj())

print(mock_obj())

print(mock_obj())


# 输出

Traceback (most recent call last):

File "D:/MyThreading/mymock.py", line 37, in <module>

print(mock_obj())

File "C:\Python36\lib\unittest\mock.py", line 939, in __call__

return _mock_self._mock_call(*args, **kwargs)

File "C:\Python36\lib\unittest\mock.py", line 998, in _mock_call

result = next(effect)

StopIteration

存在依赖关系的功能如何测试?


# 支付类

class Payment:


def requestOutofSystem(self, card_num, amount):

'''

请求第三方外部支付接口,并返回响应码

:param card_num: 卡号

:param amount: 支付金额

:return: 返回状态码,200 代表支付成功,500 代表支付异常失败

'''

# 第三方支付接口请求地址(故意写错)

url = "http://third.payment.pay/"

# 请求参数

data = {"card_num": card_num, "amount": amount}

response = requests.post(url, data=data)

# 返回状态码

return response.status_code


def doPay(self, user_id, card_num, amount):

'''

支付

:param userId: 用户ID

:param card_num: 卡号

:param amount: 支付金额

:return:

'''

try:

# 调用第三方支付接口请求进行真实扣款

resp = self.requestOutofSystem(card_num, amount)

print('调用第三方支付接口返回结果:', resp)

except TimeoutError:

# 如果超时就重新调用一次

print('重试一次')

resp = self.requestOutofSystem(card_num, amount)


if resp == 200:

# 返回第三方支付成功,则进行系统里面的扣款并记录支付记录等操作

print("{0}支付{1}成功!!!进行扣款并记录支付记录".format(user_id, amount))

return 'success'


elif resp == 500:

# 返回第三方支付失败,则不进行扣款

print("{0}支付{1}失败!!不进行扣款!!!".format(user_id, amount))

return 'fail'


# 单元测试类

class payTest(unittest.TestCase):


def test_pay_success(self):

pay = Payment()

# 模拟第三方支付接口返回200

pay.requestOutofSystem = mock.Mock(return_value=200)

resp = pay.doPay(user_id=1, card_num='12345678', amount=100)

self.assertEqual('success', resp)


def test_pay_fail(self):

pay = Payment()

# 模拟第三方支付接口返回500

pay.requestOutofSystem = mock.Mock(return_value=500)

resp = pay.doPay(user_id=1, card_num='12345678', amount=100)

self.assertEqual('fail', resp)


def test_pay_time_success(self):

pay = Payment()

# 模拟第三方支付接口首次支付超时,重试第二次成功

pay.requestOutofSystem = mock.Mock(side_effect=[TimeoutError, 200])

resp = pay.doPay(user_id=1, card_num='12345678', amount=100)

self.assertEqual('success', resp)


def test_pay_time_fail(self):

pay = Payment()

# 模拟第三方支付接口首次支付超时,重试第二次失败

pay.requestOutofSystem = mock.Mock(side_effect=[TimeoutError, 500])

resp = pay.doPay(user_id=1, card_num='12345678', amount=100)

self.assertEqual('fail', resp)

可能会有人问:第三方支付都不能用,我们的测试结果是否是有效的呢?

通常在测试一个模块的时候,是可以认为其他模块的功能是正常的,只针对目标模块进行测试是没有任何问题的,所以说测试结果也是正确的

mock装饰器

一共两种格式


@patch('module名字.方法名')

@patch.object(类名, '方法名')

# 装饰类演示

from mock import Mock, patch



# 单独的相乘函数

def multiple(a, b):

return a * b



# 单独的捕获Exception函数

def is_error():

try:

os.mkdir("11")

return False

except Exception as e:

return True



# 计算类,包含add方法

class calculator(object):

def add(self, a, b):

return a + b



# 装饰类演示 - 单元测试类

class TestProducer(unittest.TestCase):


# case执行前

def setUp(self):

self.calculator = calculator()


# mock一个函数,注意也要指定module

@patch('mock_learn.multiple')

def test_multiple(self, mock_multiple):

mock_multiple.return_value = 3

self.assertEqual(multiple(8, 14), 3)


# mock一个类对象的方法

@patch.object(calculator, 'add')

def test_add(self, mock_add):

mock_add.return_value = 3

self.assertEqual(self.calculator.add(8, 14), 3)


# mock调用方法返回多个不同的值

@patch.object(calculator, 'add')

def test_effect(self, mock_add):

mock_add.side_effect = [1, 2, 3]

self.assertEqual(self.calculator.add(8, 14), 1)

self.assertEqual(self.calculator.add(8, 14), 2)

self.assertEqual(self.calculator.add(8, 14), 3)


# mock的函数抛出Exception

@patch('os.mkdir')

def test_exception(self, mkdir):

mkdir.side_effect = Exception

self.assertEqual(is_error(), True)


# mock多个函数,注意函数调用顺序

@patch.object(calculator, 'add')

@patch('mock_learn.multiple')

def test_more(self, mock_multiple, mock_add):

mock_add.return_value = 1

mock_multiple.return_value = 4

self.assertEqual(self.calculator.add(3, 3), 1)

self.assertEqual(multiple(3, 3), 4)

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

接口自动化测试mock框架模块实战-LMLPHP

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!

接口自动化测试mock框架模块实战-LMLPHP

08-06 08:07