一.业务分析

我们这里以企业微信的添加成员业务为例,来讲解我们的自动化测试框架。
企业微信接口文档:https://work.weixin.qq.com/api/doc/90000/90135/90195
在这里插入图片描述

在这里插入图片描述

二.框架搭建实战

1.框架目录结构分析

在这里插入图片描述

  • data:存放测试用例数据的目录。
  • images:存放项目图片的目录。
  • logs:存放日志文件的目录。
  • my_config:存放配置文件的目录。
  • my_tools:存放公共方法代码的位置。
  • page_obj:存放page类代码的目录。
  • test_case:存放用例代码的位置

2.公共方法封装

(1)获取项目根路径方法封装

在这里插入图片描述

#! /usr/bin/python
# -*- coding: utf-8 -*-
import os
def get_base_dir():
    """
    获取项目根目录
    :return:
    """
    now_dir = os.getcwd()
    while True:
        now_dir_list = os.path.split(now_dir)
        now_dir = now_dir_list[0]
        if now_dir_list[1] == "xiongxiong_test_frame":
            now_dir = os.path.join(now_dir_list[0], now_dir_list[1])
            break
    return os.path.join(now_dir,'api_frame')



if __name__ == "__main__":
    print(get_base_dir())

(2)日志方法封装

在这里插入图片描述

#! /usr/bin/python
# -*- coding: utf-8 -*-

import logging
class GetLog:
    """
    自定义logging类
    方法:1.get_logger:获取一个Logger对象。
    """

    def get_logger(self, name, level, fromt, path):
        """
        获取Logger对象。
        :param name: Logger名字。
        :param level: Logger级别。
        :param fromt: Logger日志输出级别。
        :param path: 日志文件路径。
        :return: Logger对象
        """
        self.logger = logging.getLogger(name=name)
        self.logger.setLevel(logging.DEBUG)
        if not self.logger.hasHandlers():
            # 给Logger添加一个FileHandler
            file_handler = logging.FileHandler(filename=path,encoding='utf-8')
            file_handler.setLevel(level=level)
            file_handler.setFormatter(fmt=fromt)
            self.logger.addHandler(file_handler)
            # 给Logger添加一个FileHandler
            stream_handler = logging.StreamHandler()
            stream_handler.setLevel(level=level)
            stream_handler.setFormatter(fmt=fromt)
            self.logger.addHandler(stream_handler)
        return self.logger

(3)当前时间串方法封装

在这里插入图片描述

#! /usr/bin/python
# -*- coding: utf-8 -*-

from datetime import datetime

def get_now_time():
    """
    获取当前时间字符串
    :return:
    """
    now = datetime.now().strftime('%Y-%m-%d-%H%M%S')
    return now


if __name__ == "__main__":
    print(get_now_time())

3.配置文件的使用

在这里插入图片描述

#! /usr/bin/python
# -*- coding: utf-8 -*-
# create_time: 2021-02-22
import logging
import os

from api_frame.my_tools.get_dir import get_base_dir

## 目录配置

# 项目目录
BASEDIR = get_base_dir()
# 临时图片目录
IMAGESDIR = os.path.join(BASEDIR,"images")
# 测试数据目录
TESTDATA = os.path.join(BASEDIR,"data/contact_data")

## 日志配置

import os
from api_frame.my_tools.get_log import GetLog
from api_frame.my_tools.get_strtime import get_now_time
LOG_NAME = 'ROOT'
LOG_PATH = os.path.join(BASEDIR,f'logs/{get_now_time()}.log')
LOG_LEVEL = logging.INFO
LOG_FORMAT = logging.Formatter('%(asctime)s-%(levelname)s-%(message)s-%(filename)s-%(lineno)d-%(funcName)s')

# 实例化以后Logger对象
LOGGER = GetLog().get_logger(name=LOG_NAME, level=LOG_LEVEL, fromt=LOG_FORMAT, path=LOG_PATH)

4.pageobje类的封装

注意:我们本次虽然只测试添加成员接口,但该接口的用例对删除成员,添加部门,删除部门等接口有数据依赖,所以也需要将这些接口封装到page类中。

basepage基类封装

在这里插入图片描述

#! /usr/bin/python
# -*- coding: utf-8 -*-
# createtime: 2021-02-01
import requests
from requests import Session


class BasePage:
    def __init__(self,corpid=None,corpsecret=None,session=None):
        # 生成session
        if session:
            self.session = session
        else:
            if corpid is None:
                my_corpid = 'wwb0963a3a379de433'
            else:
                my_corpid = corpid
            if corpsecret is None:
                my_corpsecret = 'X-G73KTHX9is69sJcNbv-T80SyqL86EyvFUOpm4MTsg'
            else:
                my_corpsecret = corpsecret

            r = requests.get(url=f'https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={my_corpid}&corpsecret={my_corpsecret}')
            token = r.json()['access_token']
            self.session = Session()
            # 将token封装到session
            self.session.params['access_token'] = token

成员模块相关接口封装
在这里插入图片描述

#! /usr/bin/python
# -*- coding: utf-8 -*-
# createtime: 2021-02-01
import os
from typing import List

from api_frame.my_config.config import BASEDIR,LOGGER
from api_frame.page_obj.base_page import BasePage


class ContactPage(BasePage):
    """
    通讯录管理相关接口封装
    """

    def add_remenber(self,add_remenber:dict=None):
        """
        添加成员接口封装
        :param add_remenber: 添加成员接口请求数据
        :return: 接口返回对象
        """
        try:
            # 获取请求数据
            json_data = add_remenber
            # 发送请求
            r = self.session.post(url='https://qyapi.weixin.qq.com/cgi-bin/user/create',json=json_data)
            LOGGER.info('Access to "add_remenber" url https://qyapi.weixin.qq.com/cgi-bin/user/create success')
            return r
        except Exception as e:
            LOGGER.error('Access to "add_remenber" url https://qyapi.weixin.qq.com/cgi-bin/user/create failed')
            raise e

    def find_remenber(self,userid):
        """
        查询成员接口封装
        :param userid: 成员UserID。对应管理端的帐号,企业内必须唯一。不区分大小写,长度为1~64个字节
        :return: 接口返回对象
        """
        try:
            params = {
                "userid": userid
            }
            r = self.session.get(url='https://qyapi.weixin.qq.com/cgi-bin/user/get',params=params)
            LOGGER.info('Access to "find_remenber" url https://qyapi.weixin.qq.com/cgi-bin/user/get success')
            return r
        except Exception as e:
            LOGGER.error('Access to "find_remenber" url https://qyapi.weixin.qq.com/cgi-bin/user/get failed')
            raise e

    def delete_remenber(self,delete_remenber_data:dict):
        """
        删除成员接口封装
        :param delete_remenber_data: 成员UserID。对应管理端的帐号,企业内必须唯一。不区分大小写,长度为1~64个字节
        :return: 接口返回对象
        """
        try:
            params = delete_remenber_data
            r = self.session.get(url='https://qyapi.weixin.qq.com/cgi-bin/user/delete',params=params)
            LOGGER.info('Access to "delete_remenber" url https://qyapi.weixin.qq.com/cgi-bin/user/delete success')
            return r
        except Exception as e:
            LOGGER.error('Access to "delete_remenber" url https://qyapi.weixin.qq.com/cgi-bin/user/delete failed')
            raise e

部门管理接口封装
在这里插入图片描述

#! /usr/bin/python
# -*- coding: utf-8
# create_time: 2021-02-24
from api_frame.my_config.config import LOGGER
from api_frame.page_obj.base_page import BasePage


class DepartmentPage(BasePage):
    """部门管理page类"""
    def add_department(self,add_department:dict):
        """
        添加部门接口封装
        :param add_department: 添加部门请求参数
        :return: 接口响应对象
        """
        try:
            # 生成请求数据
            json_data = add_department
            # 发送请求
            r = self.session.post("https://qyapi.weixin.qq.com/cgi-bin/department/create",json=json_data)
            LOGGER.info('Access to "add_department" url https://qyapi.weixin.qq.com/cgi-bin/department/create success')
            return r
        except Exception as e:
            LOGGER.error('Access to "add_department" url https://qyapi.weixin.qq.com/cgi-bin/department/create failed')
            raise e

    def del_department(self,del_deparment:dict):
        """
        删除部门接口封装
        :param del_deparment: 删除部门请求参数
        :return: 接口响应对象
        """
        try:
            # 生成请求数据
            json_data = del_deparment
            # 发送请求
            id = del_deparment.get('id')
            r = self.session.get(url=f"https://qyapi.weixin.qq.com/cgi-bin/department/delete?id={id}")
            LOGGER.info(f'Access to "del_department" url https://qyapi.weixin.qq.com/cgi-bin/department/delete?id={id} success')
            return r
        except Exception as e:
            LOGGER.error(
                f'Access to "del_department" url https://qyapi.weixin.qq.com/cgi-bin/department/delete?id={id} failed')
            raise e


素材管理接口封装
在这里插入图片描述

#! /usr/bin/python
# -*- coding: utf-8
# create_time: 2021-02-24
import os

from api_frame.page_obj.base_page import BasePage
from api_frame.my_config.config import BASEDIR,IMAGESDIR,LOGGER

class MaterialPage(BasePage):
    """素材管理page类"""
    def up_image(self,filename:dict):
        """
        上传图片
        :return:
        """
        try:
            filename = filename.get('filename')
            filedir = os.path.join(IMAGESDIR,filename)
            with open(file=filedir,mode='rb') as f:
                file = {"file": f}
                r = self.session.post("https://qyapi.weixin.qq.com/cgi-bin/media/upload?type=image",files=file)
            LOGGER.info(
                f'Access to "up_image" url https://qyapi.weixin.qq.com/cgi-bin/media/upload?type=image success')
            return r
        except Exception as e:
            LOGGER.error(
                f'Access to "up_image" url https://qyapi.weixin.qq.com/cgi-bin/media/upload?type=image failed')
            raise e

if __name__ == "__main__":
    s = MaterialPage().up_image("my_wework.png")
    print(s.json())

5.测试用例的编写

在page类封装完成后,我们便可以使用page类中的方法来编写测试用例。编写用例时需要注意用例数据的独立性,数据在用例中生成,且用例完成后将遗留数据清除。

(1)数据文件编写

在这里插入图片描述


# 成功userid以字母开头
- add_remenber:
    userid: "ojbk0123_-@.001"
    name: "ojbk001"
    mobile: "13118170001"
    department: [1]
  expect:
    errcode: 0
    errmsg: "created"

# 成功userid以数字开头
- add_remenber:
    userid: "77ojbk0123_-@.002"
    name: "ojbk002"
    mobile: "13118170002"
    department: [1]
  expect:
    errcode: 0
    errmsg: "created"

# 请求数据只传入正确的userid,name,email,department。
- add_remenber:
    userid: "ojbk0123_-@.003"
    name: "ojbk003"
    email: "1230001@qq.com"
    department: [1]
  expect:
    errcode: 0
    errmsg: "created"


# 请求数据只传入正确的userid,name,mobile,department。且关联多个部门
- add_remenber:
    userid: "ojbk0123_-@.004"
    name: "ojbk004"
    mobile: "13118170004"
    department: [1,2]
  add_department:
    name: "ojbk004"
    parentid: 1
  expect:
    errcode: 0
    errmsg: "created"

# 请求数据只传入正确的userid,name,mobile,department,alias
- add_remenber:
    userid: "ojbk0123_-@005"
    name: "ojbk005"
    mobile: "13118170005"
    department: [1]
    alias: "ojbk005"
  expect:
    errcode: 0
    errmsg: "created"

# 请求数据只传入正确的userid,name,mobile,department,order。
- add_remenber:
    userid: "ojbk0123_-@006"
    name: "ojbk006"
    mobile: "13118170006"
    department: [1]
    order: 1
  expect:
    errcode: 0
    errmsg: "created"

# 请求数据只传入正确的userid,name,mobile,department,position。
- add_remenber:
    userid: "ojbk0123_-@007"
    name: "ojbk007"
    mobile: "13118170007"
    department: [1]
    position: "产品经理"
  expect:
    errcode: 0
    errmsg: "created"

# 请求数据只传入正确的userid,name,mobile,department,gender。
- add_remenber:
    userid: "ojbk0123_-@008"
    name: "ojbk008"
    mobile: "13118170008"
    department: [1]
    gender: 1
  expect:
    errcode: 0
    errmsg: "created"

# 请求数据只传入正确的userid,name,mobile,department,gender
- add_remenber:
    userid: "ojbk0123_-@009"
    name: "ojbk009"
    mobile: "13118170009"
    department: [1]
    gender: 2
  expect:
    errcode: 0
    errmsg: "created"

# 请求数据只传入正确的userid,name,mobile,department,email。
- add_remenber:
    userid: "ojbk0123_-@010"
    name: "ojbk010"
    mobile: "13118170010"
    department: [1]
    email: "123010@qq.com"
  expect:
    errcode: 0
    errmsg: "created"

# 请求数据只传入正确的userid,name,mobile,department,telephone。
- add_remenber:
    userid: "ojbk0123_-@011"
    name: "ojbk011"
    mobile: "13118170011"
    department: [1]
    telephone: "0519-000011"
  expect:
    errcode: 0
    errmsg: "created"

# 请求数据只传入正确的userid,name,mobile,department,is_leader_in_dept。
- add_remenber:
    userid: "ojbk0123_-@012"
    name: "ojbk012"
    mobile: "13118170012"
    department: [1]
    is_leader_in_dept: 0
  expect:
    errcode: 0
    errmsg: "created"

# 请求数据只传入正确的userid,name,mobile,department,is_leader_in_dept。
- add_remenber:
    userid: "ojbk0123_-@.013"
    name: "ojbk013"
    mobile: "13118170013"
    department: [1]
    is_leader_in_dept: 1
  expect:
    errcode: 0
    errmsg: "created"

# 请求数据只传入正确的userid,name,mobile,department,avatar_mediaid。
- add_remenber:
    userid: "ojbk0123_-@014"
    name: "ojbk014"
    mobile: "13118170014"
    department: [1]
  up_images:
    filename: my_wework.png
  expect:
    errcode: 0
    errmsg: "created"

# 请求数据只传入正确的userid,name,mobile,department,enable。
- add_remenber:
    userid: "ojbk0123_-@015"
    name: "ojbk015"
    mobile: "13118170015"
    department: [1]
    enable: 0
  expect:
    errcode: 0
    errmsg: "created"

# 请求数据只传入正确的userid,name,mobile,department,enable
- add_remenber:
    userid: "ojbk0123_-@016"
    name: "ojbk016"
    mobile: "13118170016"
    department: [1]
    enable: 1
  expect:
    errcode: 0
    errmsg: "created"

# 请求数据只传入正确的userid,name,mobile,department,external_position。
- add_remenber:
    userid: "ojbk0123_-@.017"
    name: "ojbk017"
    mobile: "13118170017"
    department: [1]
    external_position: "项目经理"
  expect:
    errcode: 0
    errmsg: "created"

# 请求数据只传入正确的userid,name,mobile,department,external_position,positionv。
- add_remenber:
    userid: "ojbk0123_-@.018"
    name: "ojbk018"
    mobile: "13118170018"
    department: [1]
    external_position: "项目经理"
    position: "产品经理"
  expect:
    errcode: 0
    errmsg: "created"

# 请求数据只传入正确的userid,name,mobile,department,address
- add_remenber:
    userid: "ojbk0123_-@.019"
    name: "ojbk019"
    mobile: "13118170019"
    department: [1]
    address: "四川省南充市营山县"
  expect:
    errcode: 0
    errmsg: "created"



# userid包含其他字符。
- add_remenber:
    userid: "ojbk0123_-@.##020"
    name: "ojbk020"
    mobile: "13118170020"
    department: [1]
  expect:
    errcode: 40003

# userid以特殊字符开头
- add_remenber:
    userid: "@ojbk0123_-@.021"
    name: "ojbk021"
    mobile: "13118170021"
    department: [1]
  expect:
    errcode: 40003

# userid为超长字符
- add_remenber:
    userid: "@ojbk0123_-@.022000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
    name: "ojbk022"
    mobile: "13118170022"
    department: [1]
  expect:
    errcode: 40058

# userid为空
- add_remenber:
    name: "ojbk023"
    mobile: "13118170023"
    department: [1]
  expect:
    errcode: 41009

# name为空
- add_remenber:
    userid: "ojbk0123_-@.024"
    mobile: "13118170024"
    department: [1]
  expect:
    errcode: 60112

# mobile和email同时为空
- add_remenber:
    userid: "ojbk0123_-@.025"
    name: "ojbk025"
    department: [1]
  expect:
    errcode: 60129

# department为空
- add_remenber:
    userid: "ojbk0123_-@.026"
    name: "ojbk026"
    mobile: "13118170026"
  expect:
    errcode: 40066

# department为不存在的部门id
- add_remenber:
    userid: "ojbk0123_-@.027"
    name: "ojbk027"
    mobile: "13118170027"
    department: [5]
  expect:
    errcode: 40066

# mobile为错误的手机格式(13位)
- add_remenber:
    userid: "ojbk0123_-@.029"
    name: "ojbk029"
    mobile: "1311812700029"
    department: [1]
  expect:
    errcode: 0

# mobile为错误的手机格式(10位)
- add_remenber:
    userid: "ojbk0123_-@.030"
    name: "ojbk030"
    mobile: "1311817030"
    department: [1]
  expect:
    errcode: 60103

# email为错误的邮箱格式
- add_remenber:
    userid: "ojbk0123_-@.031"
    name: "ojbk031"
    mobile: "13118170031"
    department: [1]
    email: "123000@"
  expect:
    errcode: 60105

# 重复的userid
- add_remenber:
    userid: "ojbk0123_-@.032"
    name: "ojbk032"
    mobile: "13118170032"
    department: [1]
  re_add_remenber:
    userid: "ojbk0123_-@.032"
    name: "ojbk0321"
    mobile: "13118170055"
    department: [1]
  expect:
    errcode: 60102

# 重复的mobile
- add_remenber:
    userid: "ojbk0123_-@.033"
    name: "ojbk033"
    mobile: "13118170033"
    department: [1]
  re_add_remenber:
    userid: "ojbk0123_-@.0331"
    name: "ojbk0331"
    mobile: "13118170033"
    department: [1]
  expect:
    errcode: 60104

# 重复的email
- add_remenber:
    userid: "ojbk0123_-@.034"
    name: "ojbk034"
    mobile: "13118170034"
    department: [1]
    email: "1230034@qq.com"
  re_add_remenber:
    userid: "ojbk0123_-@.0341"
    name: "ojbk0341"
    mobile: "13118170088"
    department: [1]
    email: "1230034@qq.com"
  expect:
    errcode: 60106

(2)用例的编写

在这里插入图片描述

#! /usr/bin/python
# -*- coding: utf-8 -*-
# create_time: 2021-02-26
import os

import pytest
import yaml

from api_frame.page_obj.contact_page import ContactPage
from api_frame.my_config.config import TESTDATA,LOGGER
from api_frame.page_obj.department_page import DepartmentPage
from api_frame.page_obj.material_page import MaterialPage


class TestAddRmenber:
    # 获取请求数据
    data_path = os.path.join(TESTDATA, "add_remenber.yaml")
    with open(file=data_path, mode='r', encoding='utf-8') as f:
        data = yaml.safe_load(f)
    def setup_class(self):
        self.contact = ContactPage()
        self.department = DepartmentPage(session=self.contact.session)
        self.materia = MaterialPage(session=self.contact.session)


    @pytest.fixture(params=data)
    def get_data(self,request):
        """
        自定义用例的前置后置方法
        :param request:
        :return:
        """
        LOGGER.info('》》》》》》》START TO EXECUTION CASE 》》》》》》')
        LOGGER.info('GENERATE TEST CASE DATA')
        # 生成用例的依赖数据
        add_department_data = request.param.get('add_department')
        up_images_data = request.param.get('up_images')
        re_add_remenber_data = request.param.get('re_add_remenber')
        if add_department_data:                                            # 是否需要临时生成部门
            r = self.department.add_department(add_department_data)
            department_id = r.json()['id']
            assert r.json()['errcode'] == 0

        if up_images_data:                                                 # 是否需要临时上传图片
            r = self.materia.up_image(up_images_data)
            avatar_mediaid = r.json().get('media_id')
            assert r.json()['errcode'] == 0
            request.param['add_remenber']['avatar_mediaid'] = avatar_mediaid

        if re_add_remenber_data:                                            # 是否需要创建两个用户
            r = self.contact.add_remenber(re_add_remenber_data)
            assert r.json()['errcode'] == 0

        yield request.param

        LOGGER.info('CLEAR TEST CASE DATA')

        # 清理用例的依赖数据,及用例数据

        if up_images_data:                                                # 清理图片上传数据
            self.avatar_mediaid = None

        if re_add_remenber_data:                                           # 清理多用户数据
            re_del_remenber_data = {}
            re_del_remenber_data['userid'] = re_add_remenber_data.get('userid')
            r = self.contact.delete_remenber(re_del_remenber_data)
            assert r.json()['errcode'] == 0

        # 清理用例创建的用户
        except_data = request.param.get('expect').get("errcode")
        if except_data == 0:
            del_remenber_data = {}
            del_remenber_data['userid'] = request.param.get('add_remenber').get('userid')
            r = self.contact.delete_remenber(del_remenber_data)
            assert r.json()['errcode'] == 0

        if add_department_data:                                           # 清理department数据
            del_department_data = {}
            del_department_data['id'] = department_id
            r = self.department.del_department(del_department_data)
            assert r.json()['errcode'] == 0
        LOGGER.error("》》》》》》》END TO EXECUTION CASE 》》》》》》")


    # @pytest.mark.parametrize("data",data)
    def test_addremenber(self,get_data):
        """添加成员接口测试用例"""
        LOGGER.info('TEST ADD_REMENBER CASE')
        # 添加成员接口测试
        add_remenber_data = get_data.get('add_remenber')
        r = self.contact.add_remenber(add_remenber_data)
        # 断言
        expect:dict = get_data.get('expect')
        errcode = expect.get('errcode')
        errmsg = expect.get('errmsg')
        if errcode:
            assert r.json()['errcode'] == errcode


if __name__ == "__main__":
    pytest.main(['-vs','test_add_remenber.py'])

到这里我们的框架基本上就搭建起来了,后续如果有更多的用例可以继续在该框架中补充。

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐