[PyTest-案例]

接口对象封装

1.requests和pymysql实现ihrm登录接口缺点 : 代码冗余度高,耦合度高,维护成本大

核心思想 : 代码分层

  • 按代码功能划分 :
    • 接口对象层 : 负责发送http请求,访问待测接口,返回响应数据
    • 测试用例层 : 调用接口,按照响应数据,断言完成测试

封装tpshop商城

  • 普通方式实现
# 导包----我的tpshop有问题,故省略url
import requests

# 创建session对象
session=requests.Session()
# 发送验证码请求get
session.get(url="")
# 发送登录请求 post
resp=session.post(url="",date={"username":"12345678","password":"123456","varify":"8888"})
# 打印响应结果
print("登录结果:",resp.json())
登录接口对象层
  1. 封装思想 :
    • 将普通方式实现代码中固定不变的,直接写到方法的实现内部
    • 将动态变化的,从参数传入
    • 将响应结果,通过返回值return
  • 示例分析:

在这里插入图片描述
在这里插入图片描述

import requests

# 封装tpshop商城,接口对象层
class TpshopLoginApi(object):
    @classmethod
    # 获取验证码---普通实现固定不变
    def get_verify(cls,session):
        session.get(url="http://tpshop-test.itcast.itheima.net/index.php?m=Home&c=User&a=verify")
    
    # 登录
    @classmethod
    def login(cls,session,login_data):
        resp=session.post(url="http://tpshop-test.itcast.itheima.net/index.php?m=Home&c=User&a=verify",
                          date=login_data)
        # 返回值
        return resp
    
# 验证:测试自己封装的接口,功能是否正常
if __name__=='__main__':
    session=requests.Session()
    # 发送获取验证码请求
    TpshopLoginApi.get_verify(session)
    login_data={"username":"12345678","password":"123456","varify":"8888"}
    # 发送登录请求
    response=TpshopLoginApi.login(session,login_data)
    print("登录结果:",response.json())

具体实现:

1.将普通实现代码中,固定不变的和动态变换的,不变的写到参数内部,动态的通过参数传过去
2.有返回值通过return做返回
3.写测试的代码进行测试
登录接口测试用例层
  • 使用pytest框架,调用自己封装的Api发送请求,获取响应结果,断言,完成接口测试!
  • 优化前
import requests

from py_tpshopFZ import TpshopLoginApi

class TestTpshopLogin(object):
    # 定义测试方法 - 登录成功
    def test01_login_success(self):
        # 创建session实例
        session=requests.Session()
        # 调用自己封装的api,发送获取验证码请求
        TpshopLoginApi.get_verify(session)
        # 准备请求体数据
        req_data={"username":"12345678","password":"123456","verify":"8888"}
        # 调用自己封装的API,发送登录请求
        resp=TpshopLoginApi.login(self,req_data)
        # 打印响应结果:
        print("登录成功:",resp.json())

        # 断言
        assert 200 == resp.status_code()
        assert 1 == resp.json().get("status")
        assert "登陆成功" in resp.json().get("msg")
        
    # 定义测试方法 - 密码错误
    def test02_pwd_err(self):
        # 创建session实例
        session = requests.Session()
        # 调用自己封装的api,发送获取验证码请求
        TpshopLoginApi.get_verify(session)
        # 准备请求体数据
        req_data = {"username": "12345678", "password": "1234561212", "verify": "8888"}
        # 调用自己封装的API,发送登录请求
        resp = TpshopLoginApi.login(self, req_data)
        # 打印响应结果:
        print("密码错误:", resp.json())

        # 断言
        assert 200 == resp.status_code()
        assert -2 == resp.json().get("status")
        assert "密码错误" in resp.json().get("msg")
        

    # 定义测试方法 - 验证码错误
    def test03_verify_err(self):
        # 创建session实例
        session = requests.Session()
        # 调用自己封装的api,发送获取验证码请求
        TpshopLoginApi.get_verify(session)
        # 准备请求体数据
        req_data = {"username": "12345678", "password": "123456", "verify": "9999"}
        # 调用自己封装的API,发送登录请求
        resp = TpshopLoginApi.login(self, req_data)
        # 打印响应结果:
        print("验证码错误:", resp.json())

        # 断言
        assert 200 == resp.status_code()
        assert 0 == resp.json().get("status")
        assert "验证码错误" in resp.json().get("msg")
  • 优化后
import requests

from py_tpshopFZ import TpshopLoginApi

class TestTpshopLogin(object):
    # 定义为类属性,使用时类属性可以用类名,实例,self,cls均可引用
    session = None    
    # 每个方法调用前都执行一次
    def setup(self):
        # 创建session实例
        self.session=requests.Session()
        
        # 调用自己封装的API,发送获取验证码请求---索引类属性语法规范
        TpshopLoginApi.get_verify(self.session)
        
    # 定义测试方法 - 登录成功
    def test01_login_success(self):
        
        # 准备请求体数据
        req_data={"username":"12345678","password":"123456","verify":"8888"}
        # 调用自己封装的API,发送登录请求
        resp=TpshopLoginApi.login(self.session,req_data)
        # 打印响应结果:
        print("登录成功:",resp.json())

        # 断言
        assert 200 == resp.status_code()
        assert 1 == resp.json().get("status")
        assert "登陆成功" in resp.json().get("msg")

    # 定义测试方法 - 密码错误
    def test02_pwd_err(self):
        # 准备请求体数据
        req_data = {"username": "12345678", "password": "1234561212", "verify": "8888"}
        # 调用自己封装的API,发送登录请求
        resp = TpshopLoginApi.login(self.session, req_data)
        # 打印响应结果:
        print("密码错误:", resp.json())

        # 断言
        assert 200 == resp.status_code()
        assert -2 == resp.json().get("status")
        assert "密码错误" in resp.json().get("msg")


    # 定义测试方法 - 验证码错误
    def test03_verify_err(self):
        # 创建session实例
        session = requests.Session()
        # 调用自己封装的api,发送获取验证码请求
        TpshopLoginApi.get_verify(session)
        # 准备请求体数据
        req_data = {"username": "12345678", "password": "123456", "verify": "9999"}
        # 调用自己封装的API,发送登录请求
        resp = TpshopLoginApi.login(self.session, req_data)
        # 打印响应结果:
        print("验证码错误:", resp.json())

        # 断言
        assert 200 == resp.status_code()
        assert 0 == resp.json().get("status")
        assert "验证码错误" in resp.json().get("msg")

搭建自动基础化框架

  • 名称apiTestFramework(package存放带码,dict存放测试报告)
  • api : 定义封装被测系统的接口
  • scripts : 定义测试用例脚本
  • data : 存放测试数据文件
  • report : 存放生成的测试报告
  • common : 存放通用工具类
  • config.py : 定义项目的配置信息
  • run_suite.py : 执行测试套件的入口

封装ihrm登录

  • 准备数据—>调接口—>得响应结果---->设断言
  • 普通实现
import requests

resp=requests.post(url="https://ihrm-java.itheima.net/api/sys/login",
                   headers={"Content-Type":"application/json"},
                   json={"mobile": "13800000002","password": "929itheima.CN032@.20250528"})

print("打印结果:",resp.json())
  • 登录接口用例层---------登录接口封装
import requests

# 定义ihrm登录接口 类
class IhrmloginApi(object):
    # 定义方法
    @classmethod
    def login(cls,rep_data):
        resp=requests.post(url="https://ihrm-java.itheima.net/api/sys/login",json=rep_data)
        return resp

    # 测试接口封装是否成功
if __name__=='__main__':
    data={"mobile": "13800000002","password": "929itheima.CN032@.20250528"}
    # 传参进行调用
    resp=IhrmloginApi.login(data)
    print("登录成功:",resp.json())
  • 登录接口测试用例层
from api.ihrm_loginapi import IhrmloginApi


class TestIhrmLogin(object):
    # 定义测试方法--登录成功
    def test_01_login_success(self):
        # 准备登录测试数据
        data = {"mobile": "13800000002","password": "929itheima.CN032@.20250528"}
        # 调用自己封装的api 获取响应结果
        resp=IhrmloginApi.login(data)
        # 打印查看
        print("登陆成功:",resp.json())
        # 断言
        assert 200 == resp.status_code
        assert True == resp.json().get('success')
        assert 10000 == resp.json().get("code")
        assert '操作成功' in resp.json().get('message')

    # 定义测试方法--手机号未注册
    def test_02_mpbile(self):
        # 准备登录测试数据
        data = {"mobile": "13800000001", "password": "929itheima.CN032@.20250528"}
        # 调用自己封装的api 获取响应结果
        resp = IhrmloginApi.login(data)
        # 打印查看
        print("手机号未注册:", resp.json())
        # 断言
        assert 200 == resp.status_code
        assert False == resp.json().get('success')
        assert 20001 == resp.json().get("code")
        assert '用户名或密码错误' in resp.json().get('message')

    # 定义测试方法--手机号未注册
    def test_02_pwd_err(self):
        # 准备登录测试数据
        data = {"mobile": "13800000002", "password": "itheima.CN032@.20250528"}
        # 调用自己封装的api 获取响应结果
        resp = IhrmloginApi.login(data)
        # 打印查看
        print("密码错误:", resp.json())
        # 断言
        assert 200 == resp.status_code
        assert False == resp.json().get('success')
        assert 20001 == resp.json().get("code")
        assert '用户名或密码错误' in resp.json().get('message')
  • 封装断言方法:
  1. 在项目目录下, 创建common目录,添加文件assert_tools.py
  2. 在文件assert_tools.py添加通用的断言函数common_assert()
  3. 实现此函数
  4. 在测试脚本层,需要断言的位置,使用这个断言函数来实现
    在这里插入图片描述
"""定义通用的工具函数 实现断言"""

# 谁调用就给一个resp
def common_assert(resp,status_code,success,code,message):
    assert status_code == resp.status_code
    assert success is resp.json().get("success")
    assert code == resp.json().get("code")
    assert message in resp.json().get("message")
  • 加入断言优化后
from api.ihrm_loginapi import IhrmloginApi
from common.assert_tools import common_assert

class TestIhrmLogin(object):
    # 定义测试方法--登录成功
    def test_01_login_success(self):
        # 准备登录测试数据
        data = {"mobile": "13800000002","password": "929itheima.CN032@.20250529"}
        # 调用自己封装的api 获取响应结果
        resp=IhrmloginApi.login(data)
        # 打印查看
        print("登陆成功:",resp.json())

        # 断言
        common_assert(resp, 200, True, 10000, "操作成功")

    # 定义测试方法--手机号未注册
    def test_02_mobile(self):
        # 准备登录测试数据
        data = {"mobile": "13800000001", "password": "929itheima.CN032@.20250529"}
        # 调用自己封装的api 获取响应结果
        resp = IhrmloginApi.login(data)
        # 打印查看
        print("手机号未注册:", resp.json())
        # 断言
        common_assert(resp, 200, False, 20001, "用户名或密码错误")

    # 定义测试方法--密码错误
    def test_03_pwd_err(self):
        # 准备登录测试数据
        data = {"mobile": "13800000002", "password": "itheima.CN032@.20250529"}
        # 调用自己封装的api 获取响应结果
        resp = IhrmloginApi.login(data)
        # 打印查看
        print("密码错误:", resp.json())
        # 断言
        common_assert(resp, 200, False, 20001, "用户名或密码错误")

    # 电话号码为空
    def test_04_mobile_is_none(self):
        # 准备登录测试数据
        data = {"mobile": "", "password": "929itheima.CN032@.20250529"}
        # 调用自己封装的api 获取响应结果
        resp = IhrmloginApi.login(data)
        # 打印查看
        print("手机号为空:", resp.json())
        # 断言
        common_assert(resp, 200, False, 20001, "用户名或密码错误")

    # 电话有特殊字符
    def test_05_mobile_have_special_char(self):
        # 准备登录测试数据
        data = {"mobile": "1213#######", "password": "929itheima.CN032@.20250529"}
        # 调用自己封装的api 获取响应结果
        resp = IhrmloginApi.login(data)
        # 打印查看
        print("手机号有特殊字符:", resp.json())
        # 断言
        common_assert(resp, 200, False, 20001, "用户名或密码错误")

    # 10位电话号码
    def test_06_mobile10(self):
        # 准备登录测试数据
        data = {"mobile": "", "password": "929itheima.CN032@.20250529"}
        # 调用自己封装的api 获取响应结果
        resp = IhrmloginApi.login(data)
        # 打印查看
        print("10位手机号", resp.json())
        # 断言
        common_assert(resp, 200, False, 20001, "用户名或密码错误")

    # 12位电话号码
    def test_07_mobile12(self):
        # 准备登录测试数据
        data = {"mobile": "111111111111", "password": "929itheima.CN032@.20250529"}
        # 调用自己封装的api 获取响应结果
        resp = IhrmloginApi.login(data)
        # 打印查看
        print("12位手机号:", resp.json())
        # 断言
        common_assert(resp, 200, False, 20001, "用户名或密码错误")

    # 密码为空
    def test_08_pwd_empty(self):
        # 准备登录测试数据
        data = {"mobile": "13800000002", "password": None}
        # 调用自己封装的api 获取响应结果
        resp = IhrmloginApi.login(data)
        # 打印查看
        print("12位手机号:", resp.json())
        # 断言
        common_assert(resp, 200, False, 20001, "用户名或密码错误")

    # 密码有特殊字符
    def test_09_pwd_have_special_char(self):
        # 准备登录测试数据
        data = {"mobile": "13800000002", "password": "929@@@@@"}
        # 调用自己封装的api 获取响应结果
        resp = IhrmloginApi.login(data)
        # 打印查看
        print("密码有特殊字符:", resp.json())
        # 断言
        common_assert(resp, 200, False, 20001, "用户名或密码错误")

    # 密码1位
    def test_10_pwd01(self):
        def test_09_pwd_have_special_char(self):
            # 准备登录测试数据
            data = {"mobile": "13800000002", "password": "9"}
            # 调用自己封装的api 获取响应结果
            resp = IhrmloginApi.login(data)
            # 打印查看
            print("密码1位:", resp.json())
            # 断言
            common_assert(resp, 200, False, 20001, "用户名或密码错误")
    # 密码100位
    def test_11_pwd100(self):
        def test_09_pwd_have_special_char(self):
            # 准备登录测试数据
            data = {"mobile": "13800000002",
                    "password": "929@@@@@@@929@@@@@@@929@@@@@@@929@@@@@@@929@@@@@@@929@@@@@@@929@@@@@@@929@@@@@@@929@@@@@@@"}
            # 调用自己封装的api 获取响应结果
            resp = IhrmloginApi.login(data)
            # 打印查看
            print("100位密码:", resp.json())
            # 断言
            common_assert(resp, 200, False, 20001, "用户名或密码错误")
    # 多参数
    def test_12_more_params(self):
        # 准备登录测试数据
        data = {"mobile": "13800000002", "password": "929itheima.CN032@.20250529","ara":"demo"}
        # 调用自己封装的api 获取响应结果
        resp = IhrmloginApi.login(data)
        # 打印查看
        print("登陆成功:", resp.json())

        # 断言
        common_assert(resp, 200, True, 10000, "操作成功")
    # 少参
    def test_13_less_params(self):
        # 准备登录测试数据
        data = {"mobile": "13800000002"}
        # 调用自己封装的api 获取响应结果
        resp = IhrmloginApi.login(data)
        # 打印查看
        print("少参password:", resp.json())

        # 断言
        common_assert(resp, 200, False, 20001, "用户名或密码错误")
    # 无参
    def test_14_no_params(self):
        # 准备登录测试数据
        data = None
        # 调用自己封装的api 获取响应结果
        resp = IhrmloginApi.login(None)
        # 打印查看
        print("少参password:", resp.json())
        # 断言
        common_assert(resp, 200, False, 99999, "抱歉")
    # 错误参数
    def test_15_err_params(self):
        # 准备登录测试数据
        data = {"abc": "13800000002","password": "929itheima.CN032@.20250529"}
        # 调用自己封装的api 获取响应结果
        resp = IhrmloginApi.login(data)
        # 打印查看
        print("无参:", resp.json())

        # 断言
        common_assert(resp, 200, False, 20001, "用户名或密码错误")
  • data有相似的值,把登录接口参数化写入json文件
[
  {
    "desc": "登录成功",
    "req_date":{
      "mobile": "13800000002",
      "password": "929itheima.CN032@.20250529"
    },
    "status_code":200,
    "success": true,
    "code": 10000,
    "message": "操作成功!"
  },
  {
    "desc": "手机号不存在",
    "req_date":{
      "mobile": "13800000001",
      "password": "929itheima.CN032@.20250529"
    },
    "status_code":200,
    "success": false,
    "code": 20001,
    "message": "用户名或密码错误"
  },
  {
    "desc":"密码错误",
   "req_date":{
      "mobile": "13800000002",
      "password": "itheima.CN032@.20250529"
    },
    "status_code":200,
    "success": false,
    "code": 20001,
    "message": "用户名或密码错误"
  },
  {
    "desc": "手机号为空",
    "req_date":{
      "mobile": null,
      "password": "929itheima.CN032@.20250529"
    },
    "status_code": 200,
    "success": false,
    "code": 20001,
    "message": "用户名或密码错误"
  },
  {
    "desc": "手机号特殊字符",
    "req_data": {"mobile": "13@######",
    "password": "929itheima.CN032@.20250529"
    },
    "status_code": 200,
    "success": false,
    "code": 20001,
    "message": "用户名或密码错误"
  },
  {
    "desc": "10位手机号",
    "req_data": {"mobile": "1323333333",
    "password": "929itheima.CN032@.20250529"
    },
    "status_code": 200,
    "success": false,
    "code": 20001,
    "message": "用户名或密码错误"
  },
  {
    "desc": "12位手机号",
    "req_data": {"mobile": "132333333322",
    "password": "929itheima.CN032@.20250529"
    },
    "status_code": 200,
    "success": false,
    "code": 20001,
    "message": "用户名或密码错误"
  },
  {
    "desc": "密码为空",
    "req_data": {"mobile": "1323333333",
    "password": null
    },
    "status_code": 200,
    "success": false,
    "code": 20001,
    "message": "用户名或密码错误"
  },
  {
    "desc": "密码有特殊字符",
    "req_data": {"mobile": "13800000002", 
                 "password": "929@@@@@"
    },
    "status_code": 200,
    "success": false,
    "code": 20001,
    "message": "用户名或密码错误"
  },
  {
    "desc": "密码1位",
    "req_data": {"mobile": "13800000002", 
                 "password": "9"
    },
    "status_code": 200,
    "success": false,
    "code": 20001,
    "message": "用户名或密码错误"
  },
  {
    "desc": "密码100位",
    "req_data": {"mobile": "13800000002", 
      "password": "929@@@@@@@929@@@@@@@929@@@@@@@929@@@@@@@929@@@@@@@929@@@@@@@929@@@@@@@929@@@@@@@929@@@@@@@"
    },
    "status_code": 200,
    "success": false,
    "code": 20001,
    "message": "用户名或密码错误"
  },
    {
    "desc": "多参",
    "req_data": {"mobile": "13800000002", 
      "password": "929itheima.CN032@.20250529",
      "ara":"demo"
    },
    "status_code": 200,
    "success": false,
    "code": 20001,
    "message": "用户名或密码错误"
  },
  {
    "desc": "少参",
    "req_data": {"mobile": "13800000002"
    },
    "status_code": 200,
    "success": false,
    "code": 20001,
    "message": "用户名或密码错误"
  },
  {
    "desc": "无参",
    "req_data":null,
    "status_code": 200,
    "success": false,
    "code": 20001,
    "message": "抱歉"
  },
  {
    "desc": "错误参数",
    "req_data": {"mobile": "13800000002", 
      "pass": "929itheima.CN032@.20250529"
    },
    "status_code": 200,
    "success": false,
    "code": 20001,
    "message": "用户名或密码错误"
  }
]

参数化

参数化的核心 : 数据驱动(用数据驱动测试用例执行)

  • 数据驱动 : 针对一个接口,只写一个测试方法,用一份测试数据文件,管理各个测试用例的测试数据.
  • 如登录接口:15条测试用例,只需要一个测试方法,对应一个含有15组数据的json文件,进行测试

回顾PyTest

  • 加法函数
  1. 提取相同含义的数据
  2. 对应数据写入参数化参数中
  3. 参数化数据格式:@pytest.mark.parametrize(“参数”,[(),(),()])
  4. 修改通用测试方法的形参
  5. 形参中传入变量
import pytest
# 回顾Pytest
def add(x,y):
    return x + y

# 提取相同含义的数值,注意格式
data=[(10,20,30),(100,200,300),(1000,2000,3000)]

# 结合pytest定义测试类
class TestAddFunc(object):
   @pytest.mark.parametrize("x,y,expect",data)
   def test_add(self,x,y,expect):
       # 调用待测函数,得实际结果
        res=add(x,y)
        assert expect == res
从json当中提取文件
  • 实现:
    1. 将测试数据组织到json文件中
    2. 封装待测函数去读取文件,转变文件格式
    3. 结合pytest去定义类
import json
# 回顾Pytest
def add(x,y):
    return x + y

# 一般所遇数据为json,故需要转换,将[{},{},{}]转换为[(),(),()]类型
def read_json():
    with open("a.json","r",encoding="utf8") as f:
        # 数据加载进json_data文件中
        json_data=json.load(f)
        data_list = []
        for data in json_data:
            # 提取值保存在变量中
            val=data.values()
            # 转元组
            tmp=tuple(val)
            data_list.append(tmp)
            print(data_list)
        
# 调用
if "__main__" == __name__:
    read_json()
# 输出:
[(1, 2, 3)]
[(1, 2, 3), (10, 20, 30)]
[(1, 2, 3), (10, 20, 30), (100, 200, 300)]
[(1, 2, 3), (10, 20, 30), (100, 200, 300), (1000, 2000, 3000)]
  • 更改后-------打印输出转换完成的列表
            val=data.values()
            # 转元组
            tmp=tuple(val)
            data_list.append(tmp)
    return data_list
    
if "__main__" == __name__:
    # 接收返回值
    res_list=read_json()
    # 打印输出列表
    print(res_list)
# 输出结果
[(1, 2, 3), (10, 20, 30), (100, 200, 300), (1000, 2000, 3000)]
  • 更改 : 文件名可能不唯一,完善文件名
# 将测试数据组织到json文件中
[{"x": 1,"y": 2,"export": 3},
{"x": 10,"y": 20,"export": 30},
{"x": 100,"y": 200,"export": 300},
{"x": 1000,"y": 2000,"export": 3000}]
--------------------------------------------------
import pytest
import json
# 回顾Pytest
def add(x,y):
    return x + y

# 一般所遇数据为json,故需要转换,将[{},{},{}]转换为[(),(),()]类型
def read_json(filename):
    with open(filename,"r",encoding="utf8") as f:
        # 数据加载进json_data文件中
        json_data=json.load(f)
        data_list = []
        for data in json_data:
            # 提取值保存在变量中
            val=data.values()
            # 转元组
            tmp=tuple(val)
            data_list.append(tmp)
        return data_list

# # 提取相同含义的数值
# data=[(10,20,30),(100,200,300),(1000,2000,3000)]
#
# 结合pytest定义测试类
class TestAddFunc(object):
	# 传入数据
   @pytest.mark.parametrize("x,y,expect",read_json("b.json"))
   def test_add(self,x,y,expect):# 通用测试方法
       # 调用待测函数,得实际结果 # 调用待测函数,得实际结果
        res=add(x,y)
        assert expect == res
  • 步骤:
  1. 将测试数据按照[(),(),()]组织到json文件中
  2. 读取json文件,将数据转换为[(),(),()]类型
  3. 在通用测试方法上一行,添加参数化装饰器@pytest.mark.parametrize(“x,y,expect”,read_json(“b.json”))
  4. 给parameterize()传参,参1:字符串类型,内容为json文件中一组数据的key.参2: [(),(),()]格式数据.
  5. 给通用测试方法添加形参,与parameterize()参1字符串的内容一致.
  6. 修改通用测试方法,内部实现,使用形参.

(不是,我也太菜了,就这么点快给我学伤了…)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值