ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Request+Unittest接口测试框架基础搭建

2021-04-07 22:57:25  阅读:125  来源: 互联网

标签:__ Unittest Request 接口 response def print self mock


request库基本使用

发送get请求

# 1、GET请求
r = requests.get('https://httpbin.org/ip')
print(r.text)

# 1.1 发送GET请求,带参数
#等同于直接访问https://httpbin.org/get?name=mikezhou
r = requests.get('https://httpbin.org/get', params={'name': 'mikezhou','age':18})
print(r.text)

# 1.2 定制请求头
header = {'user-agent': 'my-app/0.0.1'}
r = requests.get('https://httpbin.org/get', headers=header)
print(r.text)

# 1.3 发送get请求, 加proxy
proxies = {'http': 'http://127.0.0.1:8080',
           'https': 'http://127.0.0.1:8080'}
r=requests.get('https://httpbin.org/get', proxies=proxies)
print(r.text)

# 1.4 发送get请求,加鉴权 -- Basic Auth
from requests.auth import HTTPBasicAuth
r = requests.get('https://api.github.com/user', auth=HTTPBasicAuth('user', 'password'))
print(r.text)

发送post请求

# 通常,你想要发送一些编码为表单形式的数据——非常像一个 HTML 表单。
# 要实现这个,只需简单地传递一个字典给 data 参数, 数据字典在发出请求时会自动编码为表单形式
payload = {'key1': 'value1', 'key2': 'value2'}
r = requests.post('http://httpbin.org/post', data = payload)
print(r.text)

# 2.1 很多时候你想要发送的数据并非编码为表单形式的。
# 如果你传递一个 string 而不是一个 dict。
import json
payload = {'key1': 'value1', 'key2': 'value2'}
r = requests.post('https://api.github.com/some/endpoint', data=json.dumps(payload))
print(r.text)

# 2.2 此处除了可以自行对 dict 进行编码,
# 你还可以使用 json 参数直接传递,然后它就会被自动编码,下述和上述代码等价
r = requests.post('https://api.github.com/some/endpoint', json=payload)
print(r.text)

发送PUT请求

r = requests.put('https://httpbin.org/put', data={'name': 'mikezhou'})
print(r.text)

发送DELETE请求

r = requests.delete('https://httpbin.org/anything', data={'name': 'mikezhou'})
print(r.text)

获取接口返回值

r = requests.post('https://httpbin.org/anything', data={'hello': 'mikezhou'})

# 返回文本型response
print(r.text)
# 获取二进制返回值
print(r.content)

# 返回JSON串
print(r.json())

# 获取请求返回码
print(r.status_code)
#
# # 获取response的headers
print(r.headers)
#
# # 获取response的cookie
print(r.cookies.get_dict())

request保存session

# 初始化一个session对象
s = requests.Session()

# httpbin这个网站允许我们通过如下方式设置,在set后写你需要的值即可
s.get('https://httpbin.org/cookies/set/sessioncookie/mikezhou')
r = s.get('https://httpbin.org/cookies')
print(r.text)

postman代码转换为Request代码

在这里插入图片描述

开始打造一个测试框架

创建框架目录结构

在这里插入图片描述
◆ test_case:存放测试用例
◆ test_data:存放测试数据
◆ report:存放测试报告
◆ common:存放公共方法
◆ lib:存放第三方库
◆ config: 存放环境配置信息
◆ main:框架主入口

使用request封装http请求类

在这里插入图片描述

创建测试用例

第一个例子

在这里插入图片描述

import random
import unittest
from test_project.common.http_requests import HttpRequests

class TestBattal(unittest.TestCase):

    @classmethod
    def setUpClass(cls) -> None:
        cls.url = 'http://127.0.0.1:12356/'
        cls.http = HttpRequests(cls.url)

    def setUp(self) -> None:
        pass

    def tearDown(self) -> None:
        pass

    def test_001_index(self):
        '''测试访问首页'''
        response = TestBattal.http.get()
        self.assertEqual(response.status_code, 200, '请求返回非200')

    def test_002_login(self):
        '''测试登录'''
        payload = {'username': 'mikezhou', 'password': 123456}
        response = TestBattal.http.post('login',params=payload)
        # # 获取登录返回的内容
        globals()["text"] = response.text.split('\n')[1:]  # global()会返回一个字典,这个字典后面的测试方法运行时也可以访问到,用来存放全局数据
        print(globals()["text"])
        self.assertEqual(200, response.status_code, '请求返回非200')
        self.assertIn('10001', response.text, '响应不包含10001')

    def test_003_select(self):
        '''测试选择装备'''

        # 随机选择装备

        globals()["equipmentid"] = random.choice(globals()["text"]).split(':')[0]
        payload = {'equipmentid': globals()["equipmentid"]}
        response = TestBattal.http.post('selectEq',data=payload)
        self.assertEqual(200, response.status_code, '请求返回非200')
        self.assertIn('equipmentid', response.text, '响应不包含equipmentid')

    def test_004_kill(self):
        '''测试杀敌'''
        payload = {'equipmentid': globals()["equipmentid"], 'enemyid': '20001'}
        response = TestBattal.http.post('kill',data=payload)
        self.assertEqual(200, response.status_code, '请求返回非200')
        self.assertIn('win', response.text, '响应不包含win')

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

第二个例子

在这里插入图片描述

import random
import hmac
import hashlib
import json
import unittest
from test_project.common.http_requests import HttpRequests

class TestUserApi(unittest.TestCase):

    @classmethod
    def setUpClass(cls) -> None:
        cls.url = 'http://127.0.0.1:5000/'
        cls.http = HttpRequests(cls.url)
        cls.device_sn = '123456789'
        cls.os_platform = 'ios'
        cls.app_version = '1.0'
        cls.SECRET_KEY = "mikezhou"
        cls.user_id = random.randint(10, 100)  # 随机生成id
        print(cls.user_id)

    @staticmethod
    def get_token():
        '''获取token'''
        uri = '/api/get-token'
        headers = {'device_sn': TestUserApi.device_sn,
                   'os_platform': TestUserApi.os_platform,
                   'app_version': TestUserApi.app_version,
                   'Content-Type': 'application/json'}

        args = (TestUserApi.device_sn, TestUserApi.os_platform, TestUserApi.app_version)
        content = ''.join(args).encode('ascii')
        sign_key = TestUserApi.SECRET_KEY.encode('ascii')
        sign = hmac.new(sign_key, content, hashlib.sha1).hexdigest()
        data = {'sign': sign}
        response = TestUserApi.http.post(uri, data=json.dumps(data), headers=headers)
        print(response.text)
        token = response.json().get('token')
        print(token)
        return token

    def setUp(self) -> None:
        self.headers = {'device_sn': TestUserApi.device_sn,
                   'token': TestUserApi.get_token(),  # 调用静态方法获取token
                   'Content-Type': 'application/json'}
        self.playload = {'name': 'mikezhou'}

    def test_001_createUser(self):
        '''测试创建用户'''
        uri = '/api/users/{}'.format(TestUserApi.user_id) # format方法组装数据
        response = TestUserApi.http.post(uri, data=json.dumps(self.playload), headers=self.headers)
        print(response.text)
        self.assertEqual(response.status_code, 201, '请求返回非201')

    def test_002_query_users(self):
        '''测试查询用户'''
        uri = '/api/users/{}'.format(TestUserApi.user_id)
        response = TestUserApi.http.get(uri, data=json.dumps(self.playload), headers=self.headers)
        print(response.text)
        self.assertEqual(response.status_code, 200, '请求返回非200')
        self.assertIn(json.dumps(self.playload),response.text)

    def test_003_query_all_users(self):
        '''测试查询所有用户'''
        uri = '/api/users'
        response = TestUserApi.http.get(uri, data=json.dumps(self.playload), headers=self.headers)
        print(response.text)
        count = response.json().get('count')
        items = response.json().get('items')
        self.assertEqual(response.status_code, 200, '请求返回非200')
        self.assertEqual(count,len(items))  # 判断数量正确

    def test_004_update_users(self):
        '''测试更新用户'''
        uri = '/api/users/{}'.format(TestUserApi.user_id)
        self.playload = {'name': 'mikezhou_{}'.format(random.randint(1,10))}
        response = TestUserApi.http.put(uri, data=json.dumps(self.playload), headers=self.headers)
        print(response.text)
        self.assertEqual(response.status_code, 200, '请求返回非200')
        self.assertIn(json.dumps(self.playload),response.text)

    def test_005_delete_users(self):
        '''测试删除用户'''
        uri = '/api/users/{}'.format(TestUserApi.user_id)
        self.playload = {'name': 'mikezhou_{}'.format(random.randint(1,10))}
        response = TestUserApi.http.delete(uri, data=json.dumps(self.playload), headers=self.headers)
        print(response.text)
        self.assertEqual(response.status_code, 200, '请求返回非200')

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

创建按类执行测试用例的执行策略

在这里插入图片描述

'''
按指定类运行测试用例
'''

import unittest
from test_project.test_case.test_battal import TestBattal

if __name__ == '__main__':

    # 根据给定的测试类,获取其中所有以test开头的测试方法,并返回一个测试套件
    suite1 = unittest.TestLoader().loadTestsFromTestCase(TestBattal)

    # 将多个测试类加载到测试套件中
    suite = unittest.TestSuite([suite1])

    # 设置verbosity = 2,可以打印出更详细的执行信息
    unittest.TextTestRunner(verbosity=2).run(suite)

输出测试报告

下载类库

如果想要生成 HTML 格式的报告,那么就需要额外借助第三方库(如 HtmlTestRunner)来操作。HTMLTestRunner是一个第三方的unittest HTML报告库,首先我们下载HTMLTestRunner.py,并放到对应目录下。

官方原版:http://tungwaiyip.info/software/HTMLTestRunner.html
GitHub地址:https://github.com/SeldomQA/HTMLTestRunner

在这里插入图片描述

整合到自己的框架中

放到lib目录下
在这里插入图片描述

创建一个新的执行策略
在这里插入图片描述
HTMLTestRunner类说明:
◆ stream : 指定报告的路径
◆ title : 报告的标题
◆ description : 报告的描述

run()方法说明:
◆ suit : 运行的测试套件
◆ rerun :重跑次数
◆ save_last_run :是否保存最后一个结果
在这里插入图片描述

测试框架支持ws协议

首先要安装websocket类库,在common目录下新建ws_websocket.py类
在这里插入图片描述
在这里插入图片描述

ddt数据驱动

参数化测试是一种“数据驱动测试”(Data-Driven Test),在同一个方法上测试不同的参数,以覆盖所有可能的预期分支的结果。它的测试数据可以与测试行为分离,被放入到文件、数据库或者外部介质中,再由测试程序读取。

Python 标准库中的unittest 自身不支持参数化测试,为了解决这个问题,有人专门开发了两个库:一个是ddt ,一个是parameterized 。

Python可以使用xlrd库来读取Excel文件(对xls,xlsx格式文件都可以读取),使用xlwt库来生成Excel文件(只支持到excel 2003,即xls文件),另外openpyxl库同时支持读、写excel文件,且主要针对Excel2007之
后的版本(.xlsx)。

ddt例子

import ddt
import unittest

@ddt.ddt
class MyTest(unittest.TestCase):
    def setUp(self):
        pass

    def tearDown(self):
        pass

    @ddt.data([1, 2, 3, 6], [2, 3, 4, 9], [3, 4, 5, 12])
    # @ddt.data([1,2,3,6])
    @ddt.unpack
    def test_add(self, testdata1, testdata2, testdate3, exceptdata):
        sum = testdata1 + testdata2 + testdate3
        self.assertEqual(sum, exceptdata)

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

ddt数据文件例子

import ddt
import unittest


@ddt.ddt
class MyTest(unittest.TestCase):
    @classmethod
    def setUpClass(self):
        pass

    @classmethod
    def tearDownClass(self):
        pass

    def setUp(self):
        pass

    def tearDown(self):
        pass

    @ddt.file_data('data.json')
    @ddt.unpack
    def test_sum(self, value):
        a, b, sum = value.split('||')
        print(a, b, sum)
        result = int(a) + int(b)
        self.assertEqual(result, int(sum))

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

data.json

[
  "1||2||3",
  "3||2||5",
  "3||5||8"
]

在框架中集成数据驱动能力

在这里插入图片描述

在common中创建parse_excel.py类,此类可以从excel中提取数据

from openpyxl import load_workbook

class ParseExcel(object):
    def __init__(self, excelPath, sheetName):
        print(excelPath,sheetName)
        self.wb = load_workbook(excelPath)
        self.sheet = self.wb[sheetName]
        self.maxRowNum = self.sheet.max_row

    def getDatasFromSheet(self):
        dataList = []
        for line in self.sheet.rows:
            tmpList=[]
            tmpList.append(line[0].value)
            tmpList.append(line[1].value)
            dataList.append(tmpList)
        return dataList[2:]

在test_data目录下存放测试数据的excel文件
在这里插入图片描述
使用ddt实现数据驱动测试

import os
import random
import hmac
import hashlib
import json
import unittest
import ddt
from test_project.common.http_requests import HttpRequests
from test_project.common.parse_excel import ParseExcel


def get_test_data():
    '''
    从外部获取参数数据
    :return:
    '''
    path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'test_data')
    excelPath = os.path.join(path, 'test_user_api_data.xlsx')
    print(excelPath)
    sheetName = '用户参数表'
    return ParseExcel(excelPath, sheetName)

@ddt.ddt
class TestUserApiByTDD(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:
        cls.url = 'http://127.0.0.1:5000/'
        cls.http = HttpRequests(cls.url)
        cls.device_sn = '123456789'
        cls.os_platform = 'ios'
        cls.app_version = '1.0'
        cls.SECRET_KEY = "mikezhou"
        cls.user_id = random.randint(10, 100)
        print(cls.user_id)

	
    @ddt.data(*get_test_data().getDatasFromSheet())
    def test_001_createUser(self, data):
        '''测试创建用户'''
        users, exp = tuple(data)
        print(users,exp)
        uri = '/api/users/{}'.format(users)
        response = TestUserApiByTDD.http.post(uri, data=json.dumps(self.playload), headers=self.headers)
        print(response.text)
        self.assertEqual(response.status_code, 201, '请求返回非201')
        self.assertIn(exp, response.text)

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

mock

mock简单例子

  • 例子1

import requests
from unittest import mock

def send_url():
    url = 'http://127.0.0.1:51000/'
    return requests.get(url)

# response = send_url()
# print(response)

sendUrl = mock.Mock(return_value="hello world")
response = sendUrl()
print(response)

sendUrl = mock.Mock(return_value={"code": 0, "msg": "登陆成功"})
response = sendUrl()
print(response)

sendUrl = mock.Mock(side_effect=ConnectionError('URL地址不通'))
response = sendUrl()
  • 例子2
from unittest import mock
import unittest
from count import Count

class MockDemo(unittest.TestCase):

    def test_001_add(self):
        count = Count()
        count.add = mock.Mock(return_value=13)
        result = count.add(8, 8)
        print(result)
        self.assertEqual(result, 16)

    def test_002_add(self):
        count = Count()
        # 如果return_value和side_effect同时存在,则side_effect会覆盖return_value
        count.add = mock.Mock(return_value=13,side_effect=count.add)
        result = count.add(8, 8)
        print(result)
        self.assertEqual(result, 16)

if __name__ == '__main__':
    unittest.main()
class Count():
    def add(self, a, b):
        return a + b
  • 例子3, mock patch使用示例
    通过装饰器mock方法,通过装饰器mock比前面直接创建mock类更常用。
# pay.py
def zhifu():
    '''假设这里是一个支付的功能,未开发完
    支付成功返回:{"result": "success", "msg":"支付成功"}
    支付失败返回:{"result": "fail", "msg":"余额不足"}
    '''
    pass

def zhifu_statues():
    '''根据支付的结果success or fail,判断跳转到对应页面'''
    result = zhifu()
    try:
        if result["result"] == "success":
            return "支付成功"
        elif result["result"] == "fail":
            return "支付失败"
        else:
            return "未知错误异常"
    except:
        return "Error, 服务端返回异常!"
import unittest
from unittest import mock
import pay

class TestZhifuStatues(unittest.TestCase):
    '''单元测试用例'''

    @mock.patch("pay.zhifu")
    def test_01(self, mock_zhifu):
        '''测试支付成功场景'''
        # 方法一:mock一个支付成功的数据
        pay.zhifu = mock.Mock(return_value={"result": "success", "msg":"支付成功"})
        print(pay.zhifu())

        # 方法二:mock.patch装饰器模拟返回结果
        mock_zhifu.return_value = {"result": "success", "msg":"支付成功"}

        # # 根据支付结果测试页面跳转
        statues = pay.zhifu_statues()
        print(statues)
        self.assertEqual(statues, "支付成功")

if __name__ == "__main__":
    unittest.main()
  • mock 类中的方法
    前面是直接mock模块中的方法,现在讲下如果mock模块中的类的方法
# pay_class.py
class Zhifu():
    def zhifu(self):
        '''假设这里是一个支付的功能,未开发完
        支付成功返回:{"result": "success", "reason":"null"}
        支付失败返回:{"result": "fail", "reason":"余额不足"}
        reason返回失败原因
        '''
        pass

class Statues():
    def zhifu_statues(self):
        '''根据支付的结果success or fail,判断跳转到对应页面'''
        result = Zhifu().zhifu()
        try:
            if result["result"] == "success":
                return "支付成功"
            elif result["result"] == "fail":
                return "支付失败"
            else:
                return "未知错误异常"
        except:
            return "Error, 服务端返回异常!"
import unittest
from unittest import mock
from pay_class import Zhifu,Statues

class Test_zhifu_statues(unittest.TestCase):
    '''单元测试用例'''

    @mock.patch("pay_class.Zhifu")
    def test_01(self, mock_Zhifu):
        '''测试支付成功场景'''
        # 先模拟类,再模拟类的中方法
        pay_class = mock_Zhifu.return_value  # 先返回实例,对类名称替换
        # 通过实例调用方法,再对方法的返回值替换
        pay_class.zhifu.return_value = {"result": "success", "msg":"支付成功"}
        print(pay_class.zhifu())
        # 根据支付结果测试页面跳转
        statues = Statues().zhifu_statues()
        print(statues)
        self.assertEqual(statues, "支付成功")

    @mock.patch("pay_class.Zhifu.zhifu")
    def test_02(self, mock_zhifu):
        '''测试支付失败场景'''
        # 直接模拟类中的方法
        mock_zhifu.return_value = {"result": "fail", "msg": "余额不足"}
        print(mock_zhifu())
        # 根据支付结果测试页面跳转
        statues = Statues().zhifu_statues()
        print(statues)
        self.assertEqual(statues, "支付失败")

    @unittest.mock.patch.object(Zhifu, 'zhifu')
    def test_03(self, mock_obj):
        '''测试支付成功场景,另外一种Mock方式'''
        mock_obj.return_value = {"result": "success", "reason": "Mock成功了,欢呼吧!"}
        statues = Statues().zhifu_statues()
        print(statues)
        self.assertEqual(statues, "支付成功")

    def test_04(self):
        '''测试支付成功场景,最后一种Mock写法'''
        with unittest.mock.patch.object(Zhifu, 'zhifu') as mock_obj:
            mock_obj.return_value = {"result": "success", "reason": "Mock成功了,欢呼吧!"}
            statues = Statues().zhifu_statues()
            print(statues)
            self.assertEqual(statues, "支付成功")

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

mock side_effect使用示例

import unittest
import unittest.mock

class MyTest(unittest.TestCase):

    def test_except(self):
        # 1. 创建Mock()对象,传递异常对象
        mock_obj = unittest.mock.Mock(side_effect=BaseException('自定义异常'))
        # mock_obj是对象,可调用对象,用法和函数一样
        mock_obj()

    def test_list(self):
        # 1. 创建Mock()对象,传递list
        mock_obj = unittest.mock.Mock(side_effect=[1,2,3])
        # mock_obj是对象,可调用对象,用法和函数一样
        print(mock_obj())
        print(mock_obj())
        print(mock_obj())

    def test_func(self):
        def func(a, b):
            return a+b

        # 1. 创建Mock()对象,传递函数名
        mock_obj = unittest.mock.Mock(side_effect=func)
        # mock_obj是对象,可调用对象,用法和函数一样
        print(mock_obj(2, 3))

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

标签:__,Unittest,Request,接口,response,def,print,self,mock
来源: https://blog.csdn.net/qq_34680763/article/details/115278709

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有