接口自动化

一、数据库及数据库操作概念

1.数据库概述

概念
存数据的仓库,程序中数据的载体

数据库和变量都可以存储数据,二者有什么区别?
持久性不同:数据库可以持久性存储数据(将数据写入磁盘文件),而变量不能(运行在内存中)

分类:

  • 关系型数据库(MySQL、Oracle、SQLite):安全
    • 由行和列组成
  • 非关系性数据库(Redis、MongoDB):高效
  • 数据库存储结构多样,松散:键值对、列表、hash表、字符串

2.python操作数据库相关实现

驱动:

  • MySQLdb
  • MySQLclient
  • PyMySQL

二、PyMySQL基础

1.总体介绍

安装

命令行运行:
pip install pymysql

出现 Successfully install XXX字样,安装成功

校验

命令行运行
pip list

2.基本操作框架

使用pymysql链接数据库,无论增删改查哪种操作,操作流程基本一致
1.导包
2.创建链接
3.创建游标
4.核心操作
5.释放资源
import pymysql

myConn = pymysql.Connect(host="127.0.0.1", port=3306, database="test", user="root", password="root", charset="utf8")
lv = myConn.cursor()

lv.close()
myConn.close()


三、PyMySQL核心操作–增删

1.查

import pymysql

myConn = pymysql.Connect(host="127.0.0.1", port=3306, database="test", user="root", password="root", charset="utf8")
lv = myConn.cursor()
# 发送sql
sql = "select * from t_area"
lv.execute(sql)  # 执行语句
print(lv.rowcount)  # 获取行数
# row1 = lv.fetchone() # 逐行获取
# row2 = lv.fetchone()
# row3 = lv.fetchone()
# row4 = lv.fetchone()
# row5 = lv.fetchone()
# print(row1)
# print(row2)
# print(row3)
# print(row4)
# print(row5)
rows = lv.fetchall()  # 获取所有数据
print(rows)
for row in rows:
    print("每一条", row)

lv.close()
myConn.close()

2.增删改

新增


import pymysql

myConn = pymysql.Connect(host="127.0.0.1", port=3306, database="test", user="root", password="root", charset="utf8")
lv = myConn.cursor()
# 发送sql
sql = "INSERT INTO `test`.`t_area` (`area_id`, `area_name`) VALUES (2, '北京')"
lv.execute(sql)  # 执行语句
myConn.commit()
print(lv.rowcount)  # 获取行数
lv.close()
myConn.close()

修改

import pymysql

myConn = pymysql.Connect(host="127.0.0.1", port=3306, database="test", user="root", password="root", charset="utf8")
lv = myConn.cursor()
# 发送sql
sql = " UPDATE `t_area` SET `area_name` = '上海', `priority` = '0', `create_time` = null, `update_time` = null WHERE `area_id` = 2;"
lv.execute(sql)  # 执行语句
myConn.commit()
print(lv.rowcount)  # 获取行数
lv.close()
myConn.close()

删除

import pymysql

myConn = pymysql.Connect(host="127.0.0.1", port=3306, database="test", user="root", password="root", charset="utf8")
lv = myConn.cursor()
# 发送sql
sql = " DELETE from t_area where  area_id = 2"
lv.execute(sql)  # 执行语句
myConn.commit()
print(lv.rowcount)  # 获取行数
lv.close()
myConn.close()

注意

  • 增删改三种语句都是修改数据库,操作类似
  • 增删改执行完毕之后,需要执行提交操作,否则修改失败
  • 提交方式:
    • 手动提交,连接对象.commit()
    • 自动提交 autocommit = true

四、PyMySQL核心操作–事务

1.概念

事务:一台完整的业务逻辑,在这套业务逻辑中可能包含多条SQL,这些SQL执行时,要么成功要么失败

2.特征(面试常见)

  • 原子性:不可分割
    • 事务中包含的操作被看做一个逻辑单元,这个逻辑单元中的操作要么全部成功,要么全部失败
  • 一致性:
    • 事务的结果保留不变,即事务的运行并不改变数据的一致性
  • 隔离性:
    • 又称孤立性,事务的中间的中间状态对其他事务是不可见的
  • 持久性:
    • 指一个事务一旦提交成功,他对数据库中数据的改变就是永久性的

3.事务提交机制

  • 概念
    • 提交:commit,将修改写入数据库
    • 回滚:rollback,拒绝将修改写入数据库
  • 方式
    • 自动提交:创建连接时 设置autocommit = True
    • 手动提交: 连接对象.commit()

4.事务操作

前提
需要数据库类型为innoDB

-- 将引擎修改为innoDB
ALTER TABLE t_area ENGINE = INNODB

需求
异常相关分支可以由try语句来控制
流程
异常相关分支可以由try语句来控制

'''
伪代码:
try:
    sql1 = ""  # sql语句
    lv.execute(sql1)  # 执行代码
    myConn.commit()
except Exception as e:
    myConn.rollback() # 事务回滚
'''
import pymysql

myConn = pymysql.Connect(host="127.0.0.1", port=3306, database="test", user="root", password="root", charset="utf8")
lv = myConn.cursor()
try:
    sql1 = "INSERT INTO `test`.`t_area` ( `area_name`, `priority`) VALUES ('咚咚咚', '1');"
    sql2 = "INSERT INTO `test`.`t_area` ( `area_name`, `priority`) VALUES ( '嘻嘻嘻', '22','45');"

    lv.execute(sql1)
    lv.execute(sql2)
    myConn.commit()
except Exception as e:
    print(e)
    myConn.rollback()  # 事务回滚
finally:
    lv.close()
    myConn.close()

面试题

事务有哪些特征?

  • 原子性:不可分割
    • 事务中包含的操作被看做一个逻辑单元,这个逻辑单元中的操作要么全部成功,要么全部失败
  • 一致性:
    • 事务的结果保留不变,即事务的运行并不改变数据的一致性
  • 隔离性:
    • 又称孤立性,事务的中间的中间状态对其他事务是不可见的
  • 持久性:
    • 指一个事务一旦提交成功,他对数据库中数据的改变就是永久性的

五、PyMySQL工具类封装

需求
将获取连接,获取游标,以及资源释放这些重复性操作进行封装,需要时再调用

'''
实现封装
将pymysql 的常见的用法实现封装进一个专门的工具类
封装的功能: 1.获取连接 2.获取游标 3.释放资源
'''

import pymysql


# myConn = None
class DBUtils:

    # 工具函数1.获取连接
    @classmethod
    def get_conn(cls):
        myConn = pymysql.Connect(host="127.0.0.1", port=3306, database="test", user="root", password="root",
                                 charset="utf8")

    # 工具函数2.获取游标
    @classmethod
    def get_cursor(cls, conn):
        return conn.cursor()

    # 工具函数3.释放资源
    @classmethod
    def close(cls, cursor, conn):
        if cursor:
            cursor.close()
        if conn:
            conn.close()

注意
封装的工具类所在的py文件的命名要规范,不然引用不到

调用

'''
实现调用工具类中封装的功能
以查询数据库为例

'''
from pymysql_19 import DBUtils

# 导包

# 调用获取连接的工具函数
conn = DBUtils().get_conn()
# 调用获取游标的工具函数
lv = DBUtils().get_cursor(conn)
# 核心:查询为例
sql = "select * from t_area"
lv.execute(sql)
print(lv.fetchall())
# 调用释放资源的工具函数
DBUtils().close(lv, conn)


六、接口自动化测试

概念
程序驱动代替人工驱动实现的接口测试
实现方式

  • 工具(jmeter、POSTman、restclient)
  • 编码(python,Java)
    比较
  • 工具
    • 优点:1.不需要编程基础2.功能 都是封装好的,直接调用
    • 缺点:不灵活
  • 编码
    • 优点:灵活(扩展性强)
    • 缺点:1.需要编程基础2.功能需要自实现、效率偏低

七、Requests库

1.概述

概念
Request库是使用python编写的,可以调用该库的函数直接向服务发送请求,并接收响应
角色定位
jmeter的HTTP请求

安装
pip install requests

校验 
pip list

2.GET请求

'''
访问查询接口
流程:
1.根据URL 定位接口资源
2.提交测试资源
3.发送请求,提交并处理响应结果
'''
# 导包
import requests

# 一行搞定
response = requests.get("http://192.168.10.131:1234/hello?a=1")

print("状态码:", response.status_code)
print("文本信息:", response.text)

3. POST请求

# 导包
import requests

# 三要素
date = {"username": "admin", "password": "123456"}
response = requests.post("http://192.168.10.131:8888/api/private/v1/login", data=date)

print("状态码:", response.status_code)
print("文本信息:", response.text)

4.PUT请求

# 导包
import requests

# 三要素
# date = {"username": "admin", "password": "123456"}
myjson = {"id": "500"}
token = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjUwMCwicmlkIjowLCJpYXQiOjE3MjczNDE3NDIsImV4cCI6MTcyNzQyODE0Mn0.MByFjY0rIuP1TiLwuHtTf941RgmxdiIcWDuN6xH75zg"
response = requests.put("http://192.168.10.131:8888/api/private/v1/users/:id", json=myjson)

print("状态码:", response.status_code)
print("文本信息:", response.text)

5.DELETE请求

# 导包
import requests

# 三要素
response = requests.delete("http://192.168.10.131:8888/api/private/v1/users/:id", params={"id": "500"})

print("状态码:", response.status_code)
print("文本信息:", response.text)

小结

  • 四种操作代码结构基本一致
  • 区别1:函数名不同(对应请求方式)
  • 区别2:提交测试数据和参数名不同
    • Get和Delete使用params
    • post 和PUT 使用JSON (提交的是JSON数据)或data(提交的是键值对)

为什么Get、Delete、Post、PUT提交数据使用参数不一致?

  • 因为提交数据格式不同,所以使用的数举使用参数不一致
  • Get Delete 请求格式:在请求行
  • Post和PUT 请求格式:在请求体

6.response响应

相应由行头体三部分组成

import requests

response = requests.get("http://www.baidu.com")

# 行解析
print("URL", response.url)
print("URL", response.status_code)
print("-" * 80)
# 头
print("响应头", response.headers)
print("响应头", response.cookies)
print("响应头", response.encoding)
print("-" * 80)
# 体
print("文本响应体", response.text)  # 服务器传来的是文本信息如html
# print("二进制响应体", response.content)  # 服务器传来的是图片/视频/音频/ 非文本数据
# print("JSON响应体", response.json())

7.登录实现思路

7.1 cookie

import requests

# cookie  PHPSESSID
response1 = requests.get("http://192.168.10.139/index.php?m=Home&c=User&a=verify")  # 获取验证码
print("响应码", response1.status_code)
print(response1.cookies)
id = response1.cookies.get("PHPSESSID")
print(id)
print("-"*80)
data = {"username": "1912703877@qq.com", "password": "c1912703877", "verify_code": "8888"}
cookice = {"PHPSESSID": id}
# 登录接口
response2 = requests.post("http://192.168.10.139/index.php?m=Home&c=user&a=do_login", data=data, cookies=cookice)
print(response2.status_code)
print(response2.text)
print("-"*80)

# 查询订单接口
response3 = requests.get("http://192.168.10.139/Home/Order/order_list.html",cookies=cookice)
print(response3.status_code)
print(response3.text)

7.2 session

import requests

# 创建session对象
mySession = requests.session()
response1 = mySession.get("http://192.168.10.139/index.php?m=Home&c=User&a=verify")  # 获取验证码
print("响应码", response1.status_code)
print("-"*80)
data = {"username": "1912703877@qq.com", "password": "c1912703877", "verify_code": "8888"}

# 登录接口
response2 = mySession.post("http://192.168.10.139/index.php?m=Home&c=user&a=do_login", data=data)
print(response2.status_code)
print(response2.text)
print("-"*80)

# 查询订单接口
response3 = mySession.get("http://192.168.10.139/Home/Order/order_list.html")
print(response3.status_code)
print(response3.text)

八、Pytest(初级讲解–讲三次)

1.概述

pytest是python的一种第三方的单元测试框架
特点
同自带的unittest测试框架类似,相比于unittest框架使用起来更简洁,更高效
安装

推荐使用3.10
pip  install pytest==3.10

卸载

pip uninstall pytest

校验

pip list
pytest --version

2.运行

class TestLogin:
    def test_a(self):  # 以test_ 开头的是测试函数
        print("aaaa")

    def test_b(self):
        print("bbb")

# 运行方式:终端命令行中  输入pytest 文件名
# 如果想让命令行支持打印 需要使用 -s 参数 具体方法:
# pytest -s  .\test_login.py

3. setup 和 teardown

注意
这里的mytest版本过低则无法正常使用setup和teardown,因此更新了版本,但是在pytest8.0以后 setup与teardown就弃用了,需要使用setup_methodteardown_method


def setup_method(self):
    print("测试用例执行前的初始化,如:打开浏览器,加载网页...")

def setup_class(self):
    print("类执行前的初始化,如:创建日志对象,创建数据库的连接,创建接口的请求对象...")

def teardown_method(self):
    print("\n测试用例执行后的收尾操作")

def teardown_class(self):
    print("类执行后的收尾的工作,比如:销毁日志对象,销毁数据库的连接,销毁接口的请求对象")

练习

class TestLogin:

    def setup_method(self):
        print("---setup---")
    def teardown_method(self):
        print("---teardown---")

    def test_a(self):  # 以test_ 开头的是测试函数
        print("aaaa")

    def test_b(self):
        print("bbb")

# 运行方式:终端命令行中  输入pytest 文件名
# 如果想让命令行支持打印 需要使用 -s 参数 具体方法:
# pytest -s  .\test_login.py


4.配置文件

4.1 配置文件概述

应用场景
使用配置文件,可以通过配置项来选择执行哪些文件目录下的哪些测试模块
使用方式

  1. 项目下新建的script模块
  2. 将测试脚本房嫂script中
  3. pytest的配置文件放到自动化项目目录下
  4. 名称为pytest.ini
  5. 第一行内容为[pytest],后面写具体的配置参数
  6. 命令行运行时会使用该配置文件中的配置

4.2 配置文件实现

[pytest]
addopts = -s
testpaths = ./scripts
python_files = test_*.py
python_classes = Test*
python_function = test*

参数解释:
addopts = -s 表示命令行参数
testpaths , python_files , python_classes ,python_function
表示执行哪一个包下面的哪些.py结尾的文件,以及哪些前缀开头的类,以及哪些前缀开头的测试函数

注意点

  1. 配置文件是否正常的加载?
    • 通过控制台的inifile进行查看
  2. Windows中可能出现"gdk" 错误?
    • 删除ini文件中的所有中文
  3. 在工作中这个文件也需要复制粘贴?
    • 是的,一个项目只会用一次,只需要理解,会修改即可

5.数据参数化

应用场景
数据参数化可以使代码更简洁,可读性更好

import pytest


class TestLogin:
    @pytest.mark.parametrize("params",
                             [{"username": "zhangsan", "password": "123"}, {"username": "lisi", "password": "123"}])
    def test_a(self, params):
        print(params)
        print(params["username"])
        print(params["password"])

6.测试报告插件

应用场景
自动化测试脚本最终执行是通过还是不通过,需要通过测试报告进行体现
安装

pip install pytest-html == 1.21.1     在最新的2.0上有错误

使用
在配置文件总的命令参数中增加 --html=用户路径/report.html
示例

addopts = -s --html=report/report.html

7.集成思路

目的
框架化测试用例的执行
非规范化实现

import requests


class TestDemo:

    # 初始化函数
    def setup_method(self):
        self.mySession = requests.Session()

    def teardown_method(self):
        self.mySession.close()

    def test_login(self):  # 以test_ 开头的是测试函数
        data = {"": "", "": ""}
        response = self.mySession.post("http://ip:port", data)
    #     断言
        assert 200 == response.json().get("status")
    def test_order(self):
        data = {"": "", "": ""}
        # 登录
        response1 = self.mySession.post("http://ip:port", data)
        # 获取订单
        response2 = self.mySession.get("http://ip:port", data)
        #     断言
        assert 200 == response2.json().get("status")

小结:存在的问题

  • 参数化:没有使用参数化动态的导入数据
  • 封装:测试函数中的请求数据业务相关的视线高度重复

九、接口自动化框架实现

1. 接口自动化流程

  1. 需求分析
  2. 挑选需求做自动化测试的功能
  3. 设计测试用例
  4. 搭建自动化测试环境[可选]
  5. 设计自动化测试项目的框架[可选]
  6. 编写代码
  7. 执行测试用例
  8. 生成测试报告并分析结果

2.测试用例设计

3.图解

在这里插入图片描述

4. 创建框架结构

--api			#封装请求
--script 		#编写测试脚本
--data			#存放测试数据
--utils			#存放工具类
--report		#测试报告生成目录
--app.py		#存放常量
--pytest.ini	#pytest配置文件

5.测试脚本部分实现

用自动化实现对案例增删改查接口的自动化测试

import requests

from api.AreaAPI import AreaAPI
from api.AreaDBAPI import AreaDBAPI


class TestArea:

    def setup_method(self):
        self.session = requests.session()
        self.area_api = AreaAPI(self.session)

    def teardown_method(self):
        self.session.close()

    # 查询
    def test_list_area(self):
        params = {"pagenum": "1", "pagesize": "10"}
        response = self.area_api.list_area(params=params)
        print("状态码", response.status_code)
        print("响应体", response.test)

    # 新增
    def test_add_area(self):
        data = {"username": "admin", "password": "123456"}
        response = self.area_api.add_area(data=data)
        print("状态码", response.status_code)
        print("响应体", response.test)

    # 修改(编辑用户提交)
    def test_modify_area(self):
        id = AreaDBAPI.select_id_by_name("admin")
        myjson = {"id": id}
        response = self.area_api.modify_area(myjson=myjson)
        print("状态码", response.status_code)
        print("响应体", response.test)

    # 删除(删除单个用户)
    def test_remove_area(self):
        id = AreaDBAPI.select_id_by_name("admin")
        params = {"id": id}
        response = self.area_api.remove_area(params=params)
        print("状态码", response.status_code)
        print("响应体", response.test)


6.API实现

import requests

from app import BASE_URL, SERVER_URL


class AreaAPI:
    def __int__(self, session):
        self.session = session
        self.list_area_url = BASE_URL + SERVER_URL + "users"
        self.add_area_url = BASE_URL + SERVER_URL + "login"
        # 编辑用户提交
        self.modify_area_url = BASE_URL + SERVER_URL + "users/:id/state/:type"
        # 删除单个用户
        self.remove_area_url = BASE_URL + SERVER_URL + "users/:id"

    # 查询
    def list_area(self, params):
        response = self.session.get(self.list_area_url, params=params)
        return response

    # 新增
    def add_area(self, data):
        response = self.session.Post(self.add_area_url, data=data)
        return response

    # 修改
    def modify_area(self, myjson):
        response = self.session.put(self.modify_area_url, json=myjson)
        return response

    # 删除
    def remove_area(self, params):
        response = self.session.delete(self.remove_area_url, params=params)
        return response

7.API和测试脚本优化

import requests

from api.AreaAPI import AreaAPI
from api.AreaDBAPI import AreaDBAPI


class TestArea:

    def setup_method(self):
        self.session = requests.session()
        self.area_api = AreaAPI(self.session)

    def teardown_method(self):
        self.session.close()

    # 查询
    def test_list_area(self):
        params = {"pagenum": "1", "pagesize": "10"}
        response = self.area_api.list_area(params=params)
        print("状态码", response.status_code)
        print("响应体", response.test)

    # 新增
    def test_add_area(self):
        data = {"username": "admin", "password": "123456"}
        response = self.area_api.add_area(data=data)
        print("状态码", response.status_code)
        print("响应体", response.test)

    # 修改(编辑用户提交)
    def test_modify_area(self):
        id = AreaDBAPI.select_id_by_name("admin")
        myjson = {"id": id}
        response = self.area_api.modify_area(myjson=myjson)
        print("状态码", response.status_code)
        print("响应体", response.test)

    # 删除(删除单个用户)
    def test_remove_area(self):
        id = AreaDBAPI.select_id_by_name("admin")
        params = {"id": id}
        response = self.area_api.remove_area(params=params)
        print("状态码", response.status_code)
        print("响应体", response.test)

十、HttpRunner

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值