Python+Requests+Pytest接口自动化测试微信接口实例

发布时间 2023-04-14 10:26:26作者: 不上进的小猫

 

 

 

 

 pytest.ini配置文件

[pytest]
log_cli=true
log_level=NOTSET
log_format = %(asctime)s %(levelname)s %(message)s
log_date_format = %Y-%m-%d %H:%M:%S

addopts = -vs --alluredir ./temp -m 'file'

log_file = ./log/test.log
log_file_level = info
log_file_format = %(asctime)s %(levelname)s %(message)s
log_file_date_format = %Y-%m-%d %H:%M:%S

testpaths = ./testcase
python_files = test_*.py
python_classes = Test*
python_functions = test_*
markers =
    file : test_create_flag

all.py运行所有测试用例

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2023-03-21 15:59
# @Author : hexiuxiu
# @File : all.py
'''运行所有用例'''
import os

import pytest
if __name__ == '__main__':
    pytest.main()
    os.system("allure generate temp -o reports --clean")

 在common包里的assert_util.py文件是封装的断言方法

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2023-03-28 10:54
# @Author : hexiuxiu
# @File : assert_util.py
'''处理断言'''

from jsonpath import jsonpath

class AssertUtil:
    '''自定义断言'''
#     def assert__util(self,validate, res):
#         for i in validate:
#             if "eq" in i.keys():
#                 yaml_result = i.get("eq")[0]
#                 actual_result = jsonpath(res.json(), yaml_result)
#                 expect_result = i.get("eq")[1]
#                 # print(actual_result)
#                 print("实际结果:%s" % actual_result[0])
#                 print("期望结果:%s" % expect_result)
#
#                 assert actual_result[0] == expect_result

    '''另一个断言方法,更完善,更全面'''

    def assert_result(self, yaml_validate, result, res_status):
        flag_all = 0
        # 循环得到yaml文件的数据列表
        for y in yaml_validate:
            # 循环输出字典中的键值对
            for key, values in y.items():
                # 如果键等于equals
                if key == "equals":
                    flag = self.assert_equals(values, res_status, result)
                    flag_all = flag_all + flag
                elif key == "contains":
                    flag = self.assert_contains(values, result)
                    flag_all = flag_all + flag
                else:
                    print("该断言还未添加,暂不支持")
        assert flag_all == 0

    # 断言-等于
    def assert_equals(self, values, res_status, result):
        flag = 0
        for key, value in values.items():
            # 断言状态是否与预期一致
            if key == "status_code":
                if res_status != value:
                    flag = flag + 1
                    print(f"断言失败,预期状态码{value}与返回状态码{res_status}不一致")
            else:
                assert_value = jsonpath(result, f"$..{key}")
                if assert_value:
                    if value not in assert_value:
                        flag = flag + 1
                        print(f"断言失败,{key}不等于{value}")
                else:
                    flag = flag + 1
                    print(f"{key}不存在")
            return flag

    # 断言_包含
    def assert_contains(self, values, result):
        flag = 0
        # values=jsonpath(result,)
        if values not in str(result):
            flag = flag + 1
            print(f"断言失败{values}不存在{result}中")
        return flag

在common包中的yaml_util.py文件封装的是读取yaml文件的方法

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2023-03-21 17:22
# @Author : hexiuxiu
# @File : yaml_util.py
'''读取yaml文件'''
import os
import random

import yaml

class YamlUtil():
    # 获取项目路径
    def get_progect_path(self):
        realpath = os.path.dirname(__file__).split('common')[0]
        return realpath

    # 获得随机数
    def get_randon_number(self, min, max):
        return random.randint(int(min), int(max))

    #读取extract.util文件
    def read_extract_yaml(self,key):
        with open(os.path.dirname(os.path.dirname(__file__))+'/data/extract.yaml',mode='r',encoding='utf-8') as f:
            value=yaml.load(stream=f,Loader=yaml.FullLoader)
            return value[key]

    #写入extract.util文件
    def write_extract_yaml(self,data):
        with open(os.path.dirname(os.path.dirname(__file__))+'/data/extract.yaml',mode='a',encoding='utf-8') as f:
            yaml.dump(data=data,stream=f,allow_unicode=True)

    #清除extract.util文件
    def clear_extract_yaml(self):
        with open(os.path.dirname(os.path.dirname(__file__))+'/data/extract.yaml',mode='w',encoding='utf-8') as f:
            f.truncate()

    #读取testcase文件
    def read_testcase_yaml(self,yaml_name):
        with open(os.path.dirname(os.path.dirname(__file__))+'/testcase/'+yaml_name,mode='r',encoding='utf-8') as f:
            value=yaml.load(stream=f,Loader=yaml.FullLoader)
            return value

data包里是存放所有文件,1.jpg是一个图片,extract.yaml文件是保存返回的access_token等值。

log包里存放的是日志文件

reports包和temp包存放的是allure生成的报告文件

testcase包里存放所有的测试用例

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2023-03-21 15:42
# @Author : hexiuxiu
# @File : test_send_request.py
'''测试用例'''
import os

import requests
import pytest
from common import yaml_util,assert_util
from random import randint
import logging


class TestRendRequest():
    number_flag=randint(1000,9999)
    jsValue=''
    logger = logging.getLogger(__name__)

    @pytest.mark.file
    @pytest.mark.parametrize('caseinfo',yaml_util.YamlUtil().read_testcase_yaml('get_token.yaml'))
    def test_get_token(self,caseinfo):
        '''获取token值'''
        data = caseinfo['test']['request']['params']
        url = caseinfo['test']['request']['url']
        method=caseinfo['test']['request']['method']
        validate = caseinfo['test']['validate']
        rep = requests.request(method=method,url=url, params=data)
        result = rep.json()
        print(result)
        res_status = rep.status_code
        print(res_status)
        # print(result['access_token'])
        # print({'access_token':rep.json()['access_token']})

        if 'access_token' in result:
            yaml_util.YamlUtil().write_extract_yaml({'access_token':rep.json()['access_token']})
            # 断言
            # assert_util.AssertUtil().assert__util(validate,rep)
            assert_util.AssertUtil().assert_result(validate, result, res_status)
            TestRendRequest.logger.info(f'正常返回请求结果{result}')
        elif result['errcode']==45009:
            print('今天获取token次数已满,明天再试')
            TestRendRequest.logger.info('今天获取token次数已满,明天再试')
        else:
            print('异常')
            TestRendRequest.logger.error('出现其他错误,执行错误用例')

    @pytest.mark.file
    @pytest.mark.parametrize('caseinfo',yaml_util.YamlUtil().read_testcase_yaml('get_flag.yaml'))
    def test_get_flag(self,caseinfo):
        '''获取公众号已创建的标签'''
        value=yaml_util.YamlUtil().read_extract_yaml('access_token')
        url=caseinfo['test']['request']['url']+f'?access_token={value}'
        method = caseinfo['test']['request']['method']
        validate=caseinfo['test']['validate']
        rep=requests.request(method=method,url=url)
        result = rep.json()
        res_status=rep.status_code
        # print(result)
        #获取其中一组id值,用于后期编辑标签使用
        TestRendRequest.jsValue=result['tags'][1]['id']
        if 'tags' in result:
            yaml_util.YamlUtil().write_extract_yaml({'jsValue': result['tags'][1]['id']})
            assert_util.AssertUtil().assert_result(validate, result, res_status)
            TestRendRequest.logger.info('成功获取公众号已创建的标签')
            TestRendRequest.logger.info('jsValue:%s' % result['tags'][1]['id'])
        else:
            TestRendRequest.logger.error('没有获取到公众号已创建的标签')
        print(TestRendRequest.jsValue)

    # @pytest.mark.file
    @pytest.mark.parametrize('caseinfo',yaml_util.YamlUtil().read_testcase_yaml('create_flag.yaml'))
    def test_create_flag(self,caseinfo):
        '''创建标签'''
        value = yaml_util.YamlUtil().read_extract_yaml('access_token')
        url=caseinfo['test']['request']['url']+f'?access_token={value}'
        # headers = {
        #     'Content-Type': 'application/json'
        # }
        data={"tag":{"name":f"桃花{TestRendRequest.number_flag}"}}
        headers=caseinfo['test']['request']['headers']
        # data=caseinfo['test']['request']['data']
        print(data)
        method = caseinfo['test']['request']['method']
        validate=caseinfo['test']['validate']
        rep=requests.request(method=method,url=url,json=data,headers=headers)
        res_status=rep.status_code
        rep.encoding='utf-8'
        result=rep.text
        # result = rep.json()
        # result = rep.content
        assert_util.AssertUtil().assert_result(validate, result, res_status)
        TestRendRequest.logger.info('创建标签')
        print(result)

    # @pytest.mark.file
    @pytest.mark.parametrize('caseinfo', yaml_util.YamlUtil().read_testcase_yaml('edit_flag.yaml'))
    def test_edit_flag(self,caseinfo):
        '''编辑标签'''
        value = yaml_util.YamlUtil().read_extract_yaml('access_token')
        url=caseinfo['test']['request']['url']+f'?access_token={value}'
        headers = caseinfo['test']['request']['headers']
        method = caseinfo['test']['request']['method']
        validate = caseinfo['test']['validate']
        jsValue = yaml_util.YamlUtil().read_extract_yaml('jsValue')
        # data = {"tag" : {"id" :TestRendRequest.jsValue,"name" : f"taohua{TestRendRequest.number_flag}"} }
        data = {"tag" : {"id" : jsValue,"name" : f"taohua{TestRendRequest.number_flag}"} }
        rep = requests.request(method=method,url=url, json=data, headers=headers)
        result=rep.json()
        res_status=rep.status_code
        assert_util.AssertUtil().assert_result(validate,result,res_status)
        TestRendRequest.logger.info('编辑标签')
        print(rep.json())

    # @pytest.mark.file
    @pytest.mark.parametrize('caseinfo', yaml_util.YamlUtil().read_testcase_yaml('delect_flag.yaml'))
    def test_delect_flag(self,caseinfo):
        '''删除标签'''
        value = yaml_util.YamlUtil().read_extract_yaml('access_token')
        jsValue = yaml_util.YamlUtil().read_extract_yaml('jsValue')
        url=caseinfo['test']['request']['url']+f'?access_token={value}'
        headers = caseinfo['test']['request']['headers']
        method=caseinfo['test']['request']['method']
        validate = caseinfo['test']['validate']
        data = {"tag" : {"id" :jsValue} }
        rep = requests.request(method=method,url=url, json=data, headers=headers)
        result = rep.json()
        res_status = rep.status_code
        assert_util.AssertUtil().assert_result(validate, result, res_status)
        TestRendRequest.logger.info('删除标签')
        print(rep.json())

    # @pytest.mark.file
    @pytest.mark.parametrize('caseinfo', yaml_util.YamlUtil().read_testcase_yaml('updata_photo.yaml'))
    def test_file_upload(self,caseinfo):
        '''上传图片'''
        value = yaml_util.YamlUtil().read_extract_yaml('access_token')

        url = caseinfo['test']['request']['url'] + f'?access_token={value}'
        method = caseinfo['test']['request']['method']
        validate = caseinfo['test']['validate']
        # data = caseinfo['test']['request']['data']
        # print(data)
        data={
            "media":open(r'./data/1.jpg',"rb")
        }
        rep=requests.request(method=method,url=url,files=data)
        result = rep.json()
        res_status = rep.status_code
        print(rep.json())
        assert_util.AssertUtil().assert_result(validate, result, res_status)
        TestRendRequest.logger.info('上传图片')
-   test:
        name: 创建标签:正常填写参数
        request:
            headers:
                Postman-Token: 9445b6b0-4a08-42dd-ab80-4984efe4d538
                User-Agent: PostmanRuntime/7.26.8
                Content-Type: application/json
            method: POST
            url: https://api.weixin.qq.com/cgi-bin/tags/create
#            data: {"tag": {"name": 'f"{TestRendRequest.number_flag}"'}}
            #${rand_str(5, 10)}生成5到10为随机字符串
        validate:
        -   equals:
              status_code:
               200
#        -   equals:
#              errcode:
#               45056
#        -   contains: too many tag now
-   test:
        name: 创建标签:正常填写参数
        request:
            headers:
                Postman-Token: 9445b6b0-4a08-42dd-ab80-4984efe4d538
                User-Agent: PostmanRuntime/7.26.8
                Content-Type: application/json
            method: POST
            url: https://api.weixin.qq.com/cgi-bin/tags/create
#            data: {"tag": {"name": 'f"{TestRendRequest.number_flag}"'}}
            #${rand_str(5, 10)}生成5到10为随机字符串
        validate:
        -   equals:
              status_code:
               200
#        -   equals:
#              errcode:
#               45056
#        -   contains: too many tag now
-   test:
        name: 编辑标签:正常填写参数
        request:
            headers:
                Postman-Token: 9445b6b0-4a08-42dd-ab80-4984efe4d538
                User-Agent: PostmanRuntime/7.26.8
                Content-Type: application/json
            method: POST
            url: https://api.weixin.qq.com/cgi-bin/tags/update
        validate:
        -   equals:
              status_code:
               200
        -   equals:
              errcode:
               0
        -   contains: ok
-   test:
        name: 获取公众号已创建的标签:正常获取
        request:
            headers:
                Postman-Token: 9445b6b0-4a08-42dd-ab80-4984efe4d538
                User-Agent: PostmanRuntime/7.26.8
            method: GET
            url: https://api.weixin.qq.com/cgi-bin/tags/get
        validate:
        -   equals:
              status_code:
               200
        -   equals:
#            - $.tags[0].id
#            - 1
              tags[0].id:
               2
-   test:
        name: 获取Access_token值:正常填写参数
        request:
            headers:
                Postman-Token: d56d528a-8c84-4127-9df7-da8a32555898
                User-Agent: PostmanRuntime/7.26.8
            method: GET
            params:
                appid: wx6b11b3efd1cdc290
                grant_type: client_credential
                secret: 106a9c6157c4db5f6029918738f9529d
            url: https://api.weixin.qq.com/cgi-bin/token
#        extract:
#            tokens: content.access_token
        validate:
        -   equals:
              status_code:
                200
        -   contains: access_token
#              headers.Content-Type:
#                application/json; encoding=utf-8
        -   equals:
#              - $.expires_in
              expires_in:
                7200

-   test:
        name: 获取Access_token值:appid为空获取
        request:
            headers:
                Postman-Token: d56d528a-8c84-4127-9df7-da8a32555898
                User-Agent: PostmanRuntime/7.26.8
            method: GET
            params:
                appid:
                grant_type: client_credential
                secret: 106a9c6157c4db5f6029918738f9529d
            url: https://api.weixin.qq.com/cgi-bin/token
        validate:
        -   equals:
             status_code:
               200
        -   contains: appid missing rid
        -   equals:
             errcode:
               41002
-   test:
        name: 上传图片:正常填写参数
        request:
            method: POST
            url: https://api.weixin.qq.com/cgi-bin/media/uploadimg
#            data:
#              media:
#                open(r'./data/1.jpg','rb')

        validate:
        -   equals:
              status_code:
               200
        -   contains: http

注:yaml里被注释了的内容通常的是错误的

conftest.py文件里功能强大,在这里用于写夹具

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2023-03-27 10:29
# @Author : hexiuxiu
# @File : conftest.py
'''
@pytest.fixture() 一般会和 conftest.py 文件一起使用。conftest.py 文件的名称是固定的, 功能很强大。

1. conftest.py 文件是单独存放@pytest.fixture() 方法的, 用处是可以在多个py文件中共享前后置配置。

2. conftest.py 文件里面的方法在调用时不需要导入, 可以直接使用。

3. conftest.py 文件可以有多个。也可以有多个不同的层级

4. 在实际项目中,@pytest.fixture() 和 conftest.py的结合, 可以用来做登录、数据库连接等多种前置功能配置,它的好处就是多个py文件能共享,不用每个类都写一次
'''
import pytest
from common.yaml_util import YamlUtil

@pytest.fixture(scope='session',autouse=True)
def clear_yaml():
    YamlUtil().clear_extract_yaml()

关于第三方工具pytest和allure的下载可以写一个requirements.txt文件,执行该文件能一次性下载

pytest
pytest-html
pytest-xdist
pytest-ordering
pytest-rerunfailures
allure-pytest

1、命令:pip install -r requirements.txt

2、操作步骤:

第一步:用pip freeze > requirements.txt自动生成requirement.txt

  • 执行成功后,会自动生成requirement.txt文件。
  • 第二步:更换环境,分享项目的同时,带上requirement.txt文件!方便其他人配置。
  • 第三步:安装requirement.txt,执行命令即可一键安装完所需要的第三方库。