接口自动化测试项目实战

一、项目架构

1.1 项目介绍

IHRM人力资源管理系统,包含组织管理,招聘管理等等模块。

技术栈

前端:以node.js为核心的vue.js前端技术生态架构

后端:SpringBoot+SpringCloud+SpringMVC+SpringData (Spring全家桶)

           MySQL + Redis + RabbitMQ

1.2 项目架构

 1.3 接口文档

api_ihrm

二、接口文档解析

三、编写测试用例

3.1 登录接口测试用例

 

 

登录接口测试用例为单场景用例测试,因此数据应当考虑:正常数据、异常数据(类型错误、长度不足、长度过长、含有特殊符号)、参数异常(多参、少参、无参)

 3.2 员工管理测试用例

注意:员工管理测试是业务流程测试,因此只需要将整个操作完成一遍,检查接口返回数据是否为期望值。 

 四、编写代码

4.1 测试项目结构

测试项目包含:

  • api模块:封装api接口,包括employee.py、login.py,其中还包括app.py(包含host、header、logging)、utils.py(封装断言)
  • scripts模块:测试业务场景代码(testLogin、testEmployee、testLoginParam、testLoginsuc)
  • data:放测试数据
  • files:放用例以及接口文档等文件
  • log:日志
  • reports:测试报告
  • tools:工具包:HtmlRunnerTest.py
  • run_suite.py:执行测试套件,生成测试报告

4.2 log日志文件封装

import logging
import os



# app.py 初始化日志函数
# 获取当前项目路径
BASE_DIR = os.path.dirname(os.path.abspath(__file__))

def get_log():

    # 创建日志对象
    logger = logging.getLogger()
    # 设置日志等级
    logger.setLevel(logging.INFO)
    #创建handler
    now = time.strftime('%Y%m%d%H%M%S',time.localtime(time.time()))
    file_name = BASE_DIR + now + '/.log' 
    sh = logging.StreamHandler(filename = file_name,encording = 'utf-8')
    fh = logging.FileHandler(filename = file_name,encording = 'utf-8')
    
    #创建日志格式
    fmt = '%(asctime)s %(levelname)s [%(name)s] [%(filename)s(%(funcName)s:%(lineno)d)] - %                        (message)s'
    formatter = logging.Formatter(fmt)
    #将日志格式添加至handler
    fh.setFormatter(formatter)
    fh.setFormatter(formatter)
    #将handler添加至日志对象
    logger.addHandler(fh)
    logger.addHandler(sh)

4.3 添加项目信息

import logging
import os



# app.py 初始化日志函数
# 获取当前项目路径
BASE_DIR = os.path.dirname(os.path.abspath(__file__))

def get_log():

    # 创建日志对象
    logger = logging.getLogger()
    # 设置日志等级
    logger.setLevel(logging.INFO)
    #创建handler
    now = time.strftime('%Y%m%d%H%M%S',time.localtime(time.time()))
    file_name = BASE_DIR + now + '/.log' 
    sh = logging.StreamHandler(filename = file_name,encording = 'utf-8')
    fh = logging.FileHandler(filename = file_name,encording = 'utf-8')
    
    #创建日志格式
    fmt = '%(asctime)s %(levelname)s [%(name)s] [%(filename)s(%(funcName)s:%(lineno)d)] - %                        (message)s'
    formatter = logging.Formatter(fmt)
    #将日志格式添加至handler
    fh.setFormatter(formatter)
    fh.setFormatter(formatter)
    #将handler添加至日志对象
    logger.addHandler(fh)
    logger.addHandler(sh)
[HOST]
HOST = "http://ihrm2-test.itheima.net/api/sys/"
[TOKEN]
TOKEN = None
[HEADERS]
HEADERS = {"Content-type":"Aplication/json",
            "Authorization":TOKEN
            }



添加请求信息、以及员工管理所需要的token。

4.4 添加api._init_.py

该模块下整体首先执行

import logging

import app

app.get_logger()
logging.info("TEST日志器能不能正常工作")

4.5 编写登录案例

4.5.1 登录接口

在api.login.py下封装接口信息以及登录函数

import requests

class LogingAPI:
    # 初始化登录
    def __init__(self):
        url = app.HOST + "login"
    # 登录函数
    def login(self,data):
        # 如果要调用同一个类中的函数值,则使用self.变量名
        responce = requests.post(url = self.url,json = data)
if __name__ == "__main__":
    LoginAPI().login({"mobile": "13800000002", "password": "123456"})
    print(responce)

运行结果:

4.5.2 登录用例

在scripts下建立testLogin.py

import logging
import unittest

import app
from login import LoginAPI
from utils import assert_common

class TestLogin:
    # 前置处理
    @classmethod
    def setUpclass(cls)->None:
        cls.login_api = LoginAPI()
    @classmethod
    def tearDownclass(cls)->None:
        pass
    # 测试用例
    # 登陆成功
    def test_01_loginSuc(self):
        responce = self.login_api.login({"mobile": "13800000002", "password": "123456"})
        jsondata = responce.json()
        self.assertEqual(200,responce.status_code)
        self.assertEqual(True,jsondata.get("success"))
        self.assertEqual(10000,jsondata.get("code"))
        self.assertEqual("操作成功!",jsondata.get("message"))
        # 提取token
        token = "Bearer " + jsondata.get("data")
        app.TOKEN = token
        print(token)
        print(app.TOKEN)

    # 手机号为空
    def test_02_empmobile(self):
        responce = self.loging_api.login({"mobile": "", "password": "123456"})
        jsondata = responce.json()
        self.assertEqual(200,responce.status_code)
        self.assertEqual(False,jsondata.get("success"))
        self.assertEqual(20001,jsondata.get("code"))
        self.assertEqual("用户名或密码错误",jsondata.get("message"))

    # 密码为空
    def test_03_emppwd(self):
        responce = self.login_api.login({"mobile": "13800000002", "password": ""})
        jsondata = responce.json()
        self.assertEqual(200,responce.status_code)
        self.assertEqual(False,jsondata.get("success"))
        self.assertEqual(20001,jsondata.get("code"))
        self.assertEqual("用户名或密码错误",jsondata.get("message"))

    # 手机号大于11位  
    def test_04_moremob(self):
        responce = self.login_api.login({"mobile": "138000000021", "password": "123456"})
        jsondata = responce.json()
        self.assertEqual(200,responce.status_code)
        self.assertEqual(False,jsondata.get("success"))
        self.assertEqual(20001,jsondata.get("code"))
        self.assertEqual("用户名或密码错误",jsondata.get("message"))

    # 手机号小于11位 
    def test_05_lessmob(self):
        responce = self.login_api.login({"mobile": "1380000000", "password": "123456"})
        jsondata = responce.json()
        self.assertEqual(200,responce.status_code)
        self.assertEqual(False,jsondata.get("success"))
        self.assertEqual(20001,jsondata.get("code"))
        self.assertEqual("用户名或密码错误",jsondata.get("message"))

    # 手机号含有字母
    def test_06_mobcontainsletter(self):
        responce = self.login_api.login({"mobile": "1380000000m2", "password": "123456"})
        jsondata = responce.json()
        self.assertEqual(200,responce.status_code)
        self.assertEqual(False,jsondata.get("success"))
        self.assertEqual(20001,jsondata.get("code"))
        self.assertEqual("用户名或密码错误",jsondata.get("message"))
     
    # 手机号含有空格
    def test_07_mobcontainsspace(self):
        responce = self.login_api.login({"mobile": "138000000 2", "password": "123456"})
        jsondata = responce.json()
        self.assertEqual(200,responce.status_code)
        self.assertEqual(False,jsondata.get("success"))
        self.assertEqual(20001,jsondata.get("code"))
        self.assertEqual("用户名或密码错误",jsondata.get("message"))
     
    # 手机号未注册
    def test_08_unregister(self):
        responce = self.login_api.login({"mobile": "13812321236", "password": "123456"})
        jsondata = responce.json()
        self.assertEqual(200,responce.status_code)
        self.assertEqual(False,jsondata.get("success"))
        self.assertEqual(20001,jsondata.get("code"))
        self.assertEqual("用户名或密码错误",jsondata.get("message"))
     
    # 密码错误
    def test_09_errorpwd(self):
        responce = self.login_api.login({"mobile": "1380000002", "password": "12345"})
        jsondata = responce.json()
        self.assertEqual(200,responce.status_code)
        self.assertEqual(False,jsondata.get("success"))
        self.assertEqual(20001,jsondata.get("code"))
        self.assertEqual("用户名或密码错误",jsondata.get("message"))
    
    # 多参
    def test_10_moreparam(self):
        responce = self.login_api.login({"mobile": "1380000002", "password": "123456",""xx":"aa"})
        jsondata = responce.json()
        self.assertEqual(200,responce.status_code)
        self.assertEqual(True,jsondata.get("success"))
        self.assertEqual(10000,jsondata.get("code"))
        self.assertEqual("操作成功",jsondata.get("message"))
    # 少参-登录
    def test_11_lessmobpram(self):
        responce = self.login_api.login({"password": "123456"})
        jsondata = responce.json()
        self.assertEqual(200,responce.status_code)
        self.assertEqual(False,jsondata.get("success"))
        self.assertEqual(20001,jsondata.get("code"))
        self.assertEqual("用户名或密码错误",jsondata.get("message"))
    
    # 少参-密码
    def test_12_lesspwdpram(self):
        responce = self.login_api.login({"mobile": "1380000002"})
        jsondata = responce.json()
        self.assertEqual(200,responce.status_code)
        self.assertEqual(False,jsondata.get("success"))
        self.assertEqual(20001,jsondata.get("code"))
        self.assertEqual("用户名或密码错误",jsondata.get("message"))

    # 无参
    def test_13_nopram(self):
        responce = self.login_api.login({})
        jsondata = responce.json()
        self.assertEqual(200,responce.status_code)
        self.assertEqual(False,jsondata.get("success"))
        self.assertEqual(20001,jsondata.get("code"))
        self.assertEqual("用户名或密码错误",jsondata.get("message"))





4.6 封装断言 

在app模块下的utils.py中进行封装

def assert_common(self,responce,http_code,success,code,message):
    jsondata = responce.json()
    self.assertEqual(200,responce.status_code)
    self.asserEqual(success,jsondata.get("success")
    self.asserEqual(code,jsondata.get("code")
    self.asserEqual(message,jsondata.get("message")

对TestLogin进行优化

import logging
import unittest
import app
from login import LoginAPI
from utils import assert_common

class TestLogin:
    # 前置处理
    @classmethod
    def setUpclass(cls)->None:
        cls.login_api = LoginAPI()
    @classmethod
    def tearDownclass(cls)->None:
        pass
    # 测试用例
    # 登陆成功
    def test_01_loginSuc(self):
        responce = self.login_api.login({"mobile": "13800000002", "password": "123456"})
        jsondata = responce.json()
        assert_common(responce,200,True,1000,"操作成功!")
        # 提取token
        token = "Bearer " + jsondata.get("data")
        app.TOKEN = token
        print(token)
        print(app.TOKEN)

    # 手机号为空
    def test_02_empmobile(self):
        responce = self.loging_api.login({"mobile": "", "password": "123456"})
        jsondata = responce.json()
        assert_common(self,response,200,False,20001,"用户名或密码错误")

    # 密码为空
    def test_03_emppwd(self):
        responce = self.login_api.login({"mobile": "13800000002", "password": ""})
        jsondata = responce.json()
        assert_common(self,response,200,False,20001,"用户名或密码错误")

    # 手机号大于11位  
    def test_04_moremob(self):
        responce = self.login_api.login({"mobile": "138000000021", "password": "123456"})
        jsondata = responce.json()
        assert_common(self,response,200,False,20001,"用户名或密码错误")

    # 手机号小于11位 
    def test_05_lessmob(self):
        responce = self.login_api.login({"mobile": "1380000000", "password": "123456"})
        jsondata = responce.json()
        assert_common(self,response,200,False,20001,"用户名或密码错误")

    # 手机号含有字母
    def test_06_mobcontainsletter(self):
        responce = self.login_api.login({"mobile": "1380000000m2", "password": "123456"})
        jsondata = responce.json()
        assert_common(self,response,200,False,20001,"用户名或密码错误")
     
    # 手机号含有空格
    def test_07_mobcontainsspace(self):
        responce = self.login_api.login({"mobile": "138000000 2", "password": "123456"})
        jsondata = responce.json()
        assert_common(self,response,200,False,20001,"用户名或密码错误")
     
    # 手机号未注册
    def test_08_unregister(self):
        responce = self.login_api.login({"mobile": "13812321236", "password": "123456"})
        jsondata = responce.json()
        assert_common(self,response,200,False,20001,"用户名或密码错误")
     
    # 密码错误
    def test_09_errorpwd(self):
        responce = self.login_api.login({"mobile": "1380000002", "password": "12345"})
        jsondata = responce.json()
        assert_common(self,response,200,False,20001,"用户名或密码错误")
    
    # 多参
    def test_10_moreparam(self):
        responce = self.login_api.login({"mobile": "1380000002", "password": "123456",""xx":"aa"})
        jsondata = responce.json()
        assert_common(responce,200,True,1000,"操作成功!")

    # 少参-登录
    def test_11_lessmobpram(self):
        responce = self.login_api.login({"password": "123456"})
        jsondata = responce.json()
        assert_common(self,response,200,False,20001,"用户名或密码错误")
    
    # 少参-密码
    def test_12_lesspwdpram(self):
        responce = self.login_api.login({"mobile": "1380000002"})
        jsondata = responce.json()
        assert_common(self,response,200,False,20001,"用户名或密码错误")

    # 无参
    def test_13_nopram(self):
        responce = self.login_api.login({})
        jsondata = responce.json()
        assert_common(self,response,200,False,20001,"用户名或密码错误")





4.7 参数化TestLoginPara

4.7.1 参数文件

文件位json格式放在data目录下:其中包含登录数据以及断言数据

[
  {
    "desc": "登陆成功",
    "login_data": {
      "mobile": "13800000002",
      "password": "123456"
    },
    "http_code": 200,
    "success":true,
    "code":10000,
    "message":"操作成功!"
  },
  {
    "desc": "用户名为空",
    "login_data": {
      "mobile": "",
      "password": "123456"
    },
    "http_code": 200,
    "success":false,
    "code":20001,
    "message":"用户名或密码错误",
    "data":null
  },
  {
    "desc": "密码为空",
    "login_data": {
      "mobile": "13800000002",
      "password": ""
    },
    "http_code": 200,
    "success":false,
    "code":20001,
    "message":"用户名或密码错误",
    "data":null
  },
  {
    "desc": "用户名大于11位",
    "login_data": {
      "mobile": "138000000021",
      "password": "123456"
    },
    "http_code": 200,
    "success":false,
    "code":20001,
    "message":"用户名或密码错误",
    "data":null
  },
  {
    "desc": "用户名小于11位",
    "login_data": {
      "mobile": "1380000000",
      "password": "123456"
    },
    "http_code": 200,
    "success":false,
    "code":20001,
    "message":"用户名或密码错误",
    "data":null
  },
  {
    "desc": "用户名非数字",
    "login_data": {
      "mobile": "138000000m2",
      "password": "123456"
    },
    "http_code": 200,
    "success":false,
    "code":20001,
    "message":"用户名或密码错误",
    "data":null
  },
  {
    "desc": "用户名含空格",
    "login_data": {
      "mobile": "13800000 002",
      "password": "123456"
    },
    "http_code": 200,
    "success":false,
    "code":20001,
    "message":"用户名或密码错误",
    "data":null
  },
  {
    "desc": "用户名未注册",
    "login_data": {
      "mobile": "13812321236",
      "password": "123456"
    },
    "http_code": 200,
    "success":false,
    "code":20001,
    "message":"用户名或密码错误",
    "data":null
  },
  {
    "desc": "密码错误",
    "login_data": {
      "mobile": "13800000002",
      "password": "12345"
    },
    "http_code": 200,
    "success":false,
    "code":20001,
    "message":"用户名或密码错误",
    "data":null
  },
  {
    "desc": "多参",
    "login_data": {
      "mobile": "13800000002",
      "password": "123456",
      "xx": "aa"
    },
    "http_code": 200,
    "success":true,
    "code":10000,
    "message":"操作成功!",
    "data":null
  },
  {
    "desc": "少参--用户名",
    "login_data": {
      "password": "123456"
    },
    "http_code": 200,
    "success":false,
    "code":20001,
    "message":"用户名或密码错误",
    "data":null
  },
  {
    "desc": "少参--密码",
    "login_data": {
      "mobile": "13800000002"
    },
    "http_code": 200,
    "success":false,
    "code":20001,
    "message":"用户名或密码错误",
    "data":null
  },
  {
    "desc": "无参",
    "login_data": {
    },
    "http_code": 200,
    "success":false,
    "code":20001,
    "message":"用户名或密码错误",
    "data":null
  }
]

4.7.3 TestLoginPara

import json
import unittest
from scripts.testLoginsuc import LoginAPI
from parameterized import parameterized

from utils import assert_common

def build_data(self):
    file_path = '../data/login_data.json'
    data = []
    with open(file_path,encording = "utf-8") as f:
        json_data = json.load(f)
        for i in json_data:
            login_data = json_data.get("login_data")
            http_code = json_data.get("http_code")
            success = json_data.get("success")
            code = json_data.get("code")
            messsage = json_data.get("message")
        data.append((login_data,http_code,success,code,message))
    return data
class TestLoginParam:
    @classmethod
    def setUpclass(cls)->None:
        cls.login_api = LoginAPI()
    @classmethod
    def tearDownclass(cls)->None:
        pass
    @parameterized.expand(build_data)
    def test_01_login(self,login_data,http_code,success,code,message)):
        responce = login_api.login(login_data)
        print(responce.json())
        assert_commoc(login_data,http_code,success,code,message)
    
    
    

运行结果为:

 4.8 员工管理用例

4.8.1 TestLoginsue.py

员工管理必须在登陆后获得TOKEN之后才能进行操作,必须先封装登录成功步骤,封装在scripts目录下,执行员工管理测试用例前首先执行该测试。

import logging
import unittest

import app
from login import LoginAPI
from utils import assert_common

class TestLogin:
    # 前置处理
    @classmethod
    def setUpclass(cls)->None:
        cls.login_api = LoginAPI()
    @classmethod
    def tearDownclass(cls)->None:
        pass
    # 测试用例
    # 登陆成功
    def test_01_loginSuc(self):
        responce = self.login_api.login({"mobile": "13800000002", "password": "123456"})
        jsondata = responce.json()
        self.assertEqual(200,responce.status_code)
        self.assertEqual(True,jsondata.get("success"))
        self.assertEqual(10000,jsondata.get("code"))
        self.assertEqual("操作成功!",jsondata.get("message"))
        # 提取token
        token = "Bearer " + jsondata.get("data")
        app.TOKEN = token
        print(token)
        print(app.TOKEN)

4.8.2 员工管理接口

在api模块下封装员工管理接口于employee.py文件中

import requests
import app

class EmployeeAPI:
    def __init__(self):
        self.header = app.HEADER
        self.add_url = app.HOST + 'user'
        self.get_url = app.HOST + 'user/{}'
    # 添加员工
    def add_emp(self,username,password):
        data = {
                "username":username,
                "password":password,
                "timeOfEntry": "2019-12-02",
                "formOfEmployment": 1,
                "workNumber": "1234",
                "departmentName": "测试",
                "departmentId": "1210411411066695680",
                "correctionTime": "2019-12-15T16:00:00.000Z"
                }
        responce = requests.post(url = self.add_url,json=data,header=self.header)
        return responce

    # 查看员工
    def get_emp(self,id):
        recponce = requests.get(url = self.get_url.format(id),header = self.header)
        return = responce

    # 修改员工
    def update_emp(self,id,username):
        data = {"username":username}
        responce = requests.put(url = self.get_url.format(id),json = data,header = self.header)

    # 添加员工
    def del_emp(self,id):
        responce = requests.delete(url = self.get_url.format(id),header = self.haeder)
        return responce        

4.8.3 TestEmployee 

在scripts模块下写员工管理测试用例

class TestEmployee:
    @classmethod
    def setUpclass(cls)->None:
        cls.emlpoyee_api = EmployeeAPI()
    @classmethod
    def tearDown(cls)->None:
        pass
    id = None
    def test_01_add_emp(self):
        responce = self.employee_api.add_emp("熊猫44", "11116111111")
        jsondata = responce.json()
        assert_common(responce,200,True,10000,"操作成功!")
        global id
        id = jsondata.get("data").get("id")  #声明为全局变量
        print(id)
        print(jsondata)

    def test_02_get_emp(self):
        responce = self.employee_api.get_emp(id = id )
        jsondata = responce.json()
        print(jsondata)
        assert_common(responce,200,True,10000,"操作成功!")

    def test_03_update_emp(self):
        responce = self.employee_api.update_emp(id = id,username = "bb")
        jsondata = responce.json()
        assert_common(responce,200,True,10000,"操作成功!")
        print(jsondata)

    def test_04_delete_emp(self):
        responce = self.employee_api.del_emp(id = id)
        jsondata = responce.json()
        print(jsondata)        
        assert_common(responce,200,True,10000,"操作成功!")
if __name__ == "__main__":
    unittest.main()

4.9 run_suite

执行套件

4.9.1 HtmlTestRunner.py

先将该文件放入tools模块下

4.9.2 run_suite.py

将该文件放入根目录下,目的将所有的测试用例组装成测试套件并且将其加载执行。

import unittest
from scripts.testLogin import TestLogin
from scripts.testLoginparam import TestLoginParam
from scripts.testLoginsuc import TestLogin02
from scrpits.testEmplyee import TestEmployee

# 创建测试套件suite
suite = unittest.TestSuite()
# 添加测试用例
suite.addTest(unittest.makeSuite(TestLogin))
suite.addTest(unittest.makeSuite(TestLoginparam))
suite.addTest(unittest.makeSuite(TestLogin02))
suite.addTest(unittest.makeSuite(TestEmployee))
# 执行测试套件
report = "./report/report.html"
with open(report,"wb") as f:
    runner = HTMLTestRunner(f,title = "API report")
    runner.run(suite)

5 测试报告

 6 注意事项

6.1 api

将模块api设置成test sources root,在导包的时候不会出现路径错误找不到文件的情况,但是注意在其他目录下导包的时候不用加api.

 6.2 用例执行顺序

在run_suite.py中添加用例的顺序决定执行的顺序,每一个用例的名称的asccii码值决定执行顺序

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值