导图预览
在这里插入图片描述

一、什么是接口自动化

让程序代替人工生产测试数据并判断响应的结果是否符合预期。
作用:接口升级(迭代)时,会使用接口自动化测试

原则:

  • 只测试主要的或被重复使用的接口(功能测试需要所有接口)
  • 只需要设计正向数据
  • 一般建议用最少的用例覆盖最多的业务场景,比如:登录-搜索商品-加入购物车-下单-支付
  • 自动化测试可以被重复执行,多次执行
  • 自动化测试时尽量不要使用关联,可以测试某个指定接口

总结:
自动化测试是对功能测试的补充,接口升级时,可以借助于自动化测试,检查升级前的接口实现是否能够正常运行。

二、为什么要做接口自动化?

1.接口数量大,敏捷开发,团队实现接口测试,版本控制
2.有些接口工具无法实现(加密的复杂接口,签名接口等)
3.处理不同协议的接口
4.提供更简洁明了的报告
5.多接口串联,数据库验证,日志监控
6.web自动化+接口自动化同时做的时候。

三、python+requests模块

requests第三方库,主要用于发送http请求,做接口自动化。

get请求通过params传递参数;
post请求通过json或者data传递参数。

json和data的区别:

json:
数据报文:不管是dict还是str类型,默认都是application/json,格式:{“a”:1,"b":2}
一般只能传dict格式的。

data:
数据报文:dict字典类型,那么默认情况下请求头:application/x-www-form-urlencoded; 以form表单的方式传参,格式:a=1&b=2
数据报文:str类型,那么默认情况下是:text/plain

若在其他情况下,没法直接传参,那么可以将对应的参数转换为属性支持的参数格式:

  • json.dumps(data) 序列化:将字典格式的数据转换成str格式
  • json.loads(data) 反序列化:把str格式转换成字典格式

通过requests做接口自动化

import requests
from pprint import pprint

res=requests.get('http://localhost/mgr/ps/mgr/index.html#/')

if res.status_code==200:
    print('测试通过')
else:
    print('接口存在问题')

print(res.json()) #将结果以json形式展示

import requests
from pprint import pprint

res=requests.post('http://localhost/apijson/mgr/sq_mgr/',
                  data={
  "action" : "add_course",
  "data"  : '''{
    "name":"初中121323化学",
    "desc":"初中121323化学课程",
    "display_idx":"4"
  }
  '''


})

retobj=res.json()
if retobj['retcode']==0:
    print('测试通过')
else:
    print('接口存在问题')

pprint(res.json(),width=30)

带请求头和cookies的接口需要cookie鉴权,有两种方式:
1.网页的接口基本上都要做cookie鉴权
2.通过session实现cookie鉴权----常用方式

封装请求

接口自动化框架封装的第一步,统一请求方式。
requests.request()
例:

req=requests.request("post",url=url,data=data,headers=headers)

案例:
框架封装:统一请求和通过session进行cookie鉴权


class Test_phpwind:
    cookies=""
    csrf_token=""
    #设置一次会话session,相当于保存一个身份鉴权,用于整个会话访问过程
    session=requests.session()

    def test_firstPage():
        url='http://47.107.116.139/phpwind/'
        req=Test_phpwind.session.request("get",url=url)
        # print(req.text)

        reg='name="csrf_token" value="(.*?)"'
        Test_phpwind.csrf_token=re.search(reg,req.text)[1]
        print(Test_phpwind.csrf_token)
        Test_phpwind.cookies=req.cookies
        print(Test_phpwind.cookies)

    def test_login():
        url="http://47.107.116.139/phpwind/index.php?m=u&c=login&a=dorun"
        data={
            "username":"ashley",
            "password":"zxcvbnm...",
            "backurl":"http://47.107.116.139/phpwind/",
            "invite":"",
            "csrf_token":Test_phpwind.csrf_token
        }
        headers={
            "Accept": "application/json, text/javascript, */*; q=0.01",
            "X-Requested-With": "XMLHttpRequest"

        }
        print("+"*10)
        print(Test_phpwind.csrf_token)
        print(Test_phpwind.cookies)
        req=Test_phpwind.session.request("post",url=url,data=data,headers=headers)
        print("----------------")
        print(req.json())

if __name__=="__main__":
    # pytest.main(['-vs'])
    Test_phpwind.test_firstPage()
    Test_phpwind.test_login()

接口关联的封装

通过一个关联的yaml文件进行封装。
实现:将第一步的token取出写入yaml文件。然后需要用到token的地方从yaml中读取出即可。

主要case代码:
test.py

import re
import requests
from readYaml import readYaml

class Test_phpwind:

    session=requests.session()

    def test_firstPage():
        url='http://47.107.116.139/phpwind/'
        req=Test_phpwind.session.request("get",url=url)
        # print(req.text)

        reg='name="csrf_token" value="(.*?)"'
        csrf_token=re.search(reg,req.text)[1]
        data={"csrf_token":csrf_token}

        # 为什么穿不了参数,提示没有传data,实际上有了呀?
        # readYaml.readYaml.write_contract_yaml(data)
        readYaml().write_contract_yaml(data)
        
    def test_login():
         # 整个一直报错是因为从yaml中获取到的是一个dict类型,需要获取token值即可
        # csrf_token=readYaml().read_contract_yaml("csrf_token")
        
        csrf_token=readYaml().read_contract_yaml("csrf_token")['csrf_token']
        print("="*10)
        print(csrf_token)
        url="http://47.107.116.139/phpwind/index.php?m=u&c=login&a=dorun"
        data={
            "username":"ashley",
            "password":"zxcvbnm...",
            "backurl":"http://47.107.116.139/phpwind/",
            "invite":"",
            "csrf_token":csrf_token
        }
        headers={
            "Accept": "application/json, text/javascript, */*; q=0.01",
            "X-Requested-With": "XMLHttpRequest"

        }
        print("+"*10)
      req=Test_phpwind.session.request("post",url=url,data=data,headers=headers)
        print("----------------")
        print(req.json())

if __name__=="__main__":
    # pytest.main(['-vs','--reruns=3'])
    Test_phpwind.test_firstPage()
    Test_phpwind.test_login()

yaml文件的读写:
readYaml.py

import os
import yaml

class readYaml:


    #读取yaml文件
    def read_contract_yaml(self,params):
        with open(os.getcwd()+'/contract.yaml',mode='r',encoding="utf-8") as f:
            value=yaml.load(stream=f,Loader=yaml.FullLoader)
            return value;

    #写入yaml文件内容
    def write_contract_yaml(self,data):
        with open(os.getcwd()+"/contract.yaml",mode='w',encoding='utf-8') as f:
            yaml.dump(data=data,stream=f,allow_unicode=True)


    def clear_yaml():
        with open(os.getcwd()+"/contract.yaml",mode='r',encoding='utf-8') as f:
            f.truncate()
            print("清除yaml内容")

conftest文件

主要是用来写pytest.fixture()方法的。
详细介绍见:https://editor.csdn.net/md/?articleId=126467461

conftest.py
import pytest
import readYaml

@pytest.fixture(scope="function")
def conn_database():
    print('连接数据库')
    yield
    print("关闭数据库")

def clear_yaml():
    readYaml.readYaml.clear_yaml()

断言

通过断言来判断接口是否通

def test_login(self):

        # 整个一直报错是因为从yaml中获取到的是一个dict类型,需要获取token值即可
        # csrf_token=readYaml().read_contract_yaml("csrf_token")
        
        csrf_token=readYaml().read_contract_yaml("csrf_token")['csrf_token']
        print("="*10)
        print(csrf_token)
        url="http://47.107.116.139/phpwind/index.php?m=u&c=login&a=dorun"
        data={
            "username":"ashley",
            "password":"zxcvbnm...",
            "backurl":"http://47.107.116.139/phpwind/",
            "invite":"",
            "csrf_token":csrf_token
        }
        headers={
            "Accept": "application/json, text/javascript, */*; q=0.01",
            "X-Requested-With": "XMLHttpRequest"
        }

        req=Test_phpwind.session.request("post",url=url,data=data,headers=headers)
        print(req.json())
        result=req.json()
        assert result["state"] in "success"
        assert 'refresh' in result

yaml文件

yaml主要用于配置文件,或者用例文件或者保存接口关联的值。主要的数据有以下两种:

  • map对象:键值对:name:value
  • 列表类型:用 - 开头的
- name:xxx
- request:xxx
- url:xxx
yaml文件的规则

1.必须包含一级关键字:name,request,validate
2.在request关键字下必须包括method、url、data,如果没有data的话,那么输出默认值。
3.提取变量使用以及关键字:extract 。支持json提取和正则提取(.+?)或(.*?),取值使用{{ }}
4.可以使用热加载的方式调用debug_talk.py中的DebugTalk类里面的方法。通过${ }调用方法
5.支持equals,contains两种断言
6.使用parameters做csv文件的数据驱动,通过$csv{appid}这种格式取值
例:

parameters:
		name-appid-secret-grant_type-assert_str:data/get_token_data.csv
- 
  name:获取鉴权码token
  request:
    method:get
    url:
    data:
      grant_type:xxx
  validate:
    - equals:{status_code:200}
    - contains:asscess_token

语法规则:
1、区分大小写
2、支持注释,用#注释
3、使用缩进判断层级关系

对接口参数的判断

在这里插入图片描述

使用DDT实现自动化

DDT(data -driven tests)数据驱动框架,指的是在自动化测试中处理测试数据的方式。
一般是测试数据与功能函数分离,存储在功能函数的外部位置。在自动化测试运行时,数据驱动框架会读取数据源中的数据,把数据作为参数传递到功能函数中,然后根据数据的条数多次运行同一个功能函数。

数据驱动的数据源可以时csv、excel、txt、数据集合以及数据库等形式。

优点:

  • 能够减少代码重复
  • 一条用例失败,不会影响其他测试数据对应的用例

DDT的使用步骤:
1、使用@ddt装饰你的测试类
2、使用@data或@file_data装饰需要数据驱动的测试方法
3、如果一组数据中有多个参数,可以通过@unpack装饰测试方法来达到解包效果,然后通过变量来接收

代码实现:

from ddt import ddt,data
@ddt      #ddt一定是装饰在TestCase的子类上
class Test_phpwind(unittest.TestCase):

    session=requests.session()
    host='http://120.55.190.222:9090'

    #测试登录接口
    #获取excel数据,*号代表返回的是可变参数
    @data(*ExcelUtils().get_excelData())
    @unpack   #对每一组数据,如果是list或tuple形式,将他们分拆成独立的参数
    def test_01_login(self,index,username,pwd,ranCode):
        print(index,username,pwd,ranCode)

        url1=self.host+"/loginController.do?login2"
        url=self.host+"/loginController.do?checkuser"
        data={
            "returnUrl": None,
            "userName":username,
            "password":pwd,
            "randCode":ranCode
        }
        headers={
            "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
            "X-Requested-With": "XMLHttpRequest"
        }

        req1=self.session.request("post",url=url1,data=data,headers=headers)
        req2=self.session.request("post",url=url,data=data,headers=headers)

        print( req2.content.decode("utf-8"))
        result=json.loads(req2.content.decode("utf-8"))

        print(result['msg'])
        if index==1:
            assert '操作成功' in result['msg']
            print(result['msg'])
        else:
            assert bool("false")  in result['success']
            print(result['msg'])

注意:data中的参数函数返回值一定需要遵守ddt.data()可接受的数据格式,即:一组数据,每个数据为单个的值;多组数据,每组数据为一个列表或一个字典。

如果是通过传入文件(json或yaml文件)提取数据,则通过@filedata()来装饰方法。@filedata(‘test_data.json’)
拓展见:https://www.jianshu.com/p/e5dcdf4d9e77

接口自动化需要了解的知识:
在这里插入图片描述

httprunner知识见:https://editor.csdn.net/md?not_checkout=1&spm=1011.2124.3001.6192&articleId=134847369

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐