基本框架的主要目录结构
image.png

配置虚拟环境

默认打开项目,没有配置环境。手动配置一下
image.png
找到python解释器的配置
image.png

点击【add】添加新的环境配置
image.png
选择虚拟环境
image.png
点击创建。创建成功之后可以看到项目使用的python为虚拟环境的python。
image.png
项目中 venv
image.png

创建common 包

image.png
创建包,因为现在是git项目,会提示是否添加到git中,选择【ADD】
image.png

定义常用函数

常用工具函数 获取项目的根目录,以及生成测试手机号,测试身份证id这些函数。
common包下创建一个 file_handler.py 这个文件中存放一些文件路径相关操作的函数。

  1. import os
  2. def get_root_dir():
  3. """
  4. 生成项目的根目录
  5. :return:
  6. """
  7. # 生成当前文件的绝对路径
  8. p1 = os.path.abspath(__file__)
  9. p2 = os.path.dirname(p1)
  10. p3 = os.path.dirname(p2)
  11. return p3
  12. if __name__ == '__main__':
  13. root_dir = get_root_dir()
  14. print(f"项目根目录 {root_dir}")

image.png

提交到代码仓库

文件编写成功之后,要及时提交代码。
选择提交按钮
image.png
输入对应的提交信息。
image.png
如果提交的时候,选择的是【Commit】,没有推送到服务器。
那么手动推动到服务器(如果上面一步已经提交并推送过了,就不需要再次push)

点击【Git】—【Log】—【Master】—右键—【Push】 推送到服务器
image.png
image.png
推送成功之后,可以看到 已经提交成功。
image.png

生成随机手机号码

按照上面的操作,创建新文件
common/my_tools.py 将生成手机号码封装到函数中

  1. """
  2. 一些常用的工具
  3. """
  4. # 导入Python内置的random 模块
  5. import random
  6. def get_phone():
  7. # 随机从列表中选择一个值
  8. nums = ["132", "130", "131", "133", "134", "135", "136", "137", "138", "139",
  9. "155", "157", "159"]
  10. # 随机从列表中选择一个元素 作为手机号的前3位。
  11. num = random.choice(nums)
  12. # 定义空的字符串
  13. p8 = ""
  14. # 生成8个随机数字
  15. for i in range(8):
  16. # 每次循环都生成一个随机数字
  17. n = random.randint(0, 9)
  18. # 将数字n跟 p8 进行拼接 因为是n是数字,将n转换位字符串进行拼接
  19. p8 = p8 + str(n)
  20. return num+p8
  21. if __name__ == '__main__':
  22. phone = get_phone()
  23. print(phone)

代码测试无误,可以进行提交
image.png

生成身份证id

创建 config 配置包

config 主要用来存放一些配置文件。
将gb2260.csv文件放在config 配置包中。
image.png

编写生成身份证id函数

在common/mytools.py 文件中 生成随机的身份证号。

  1. """
  2. 一些常用的工具
  3. """
  4. # 导入Python内置的random 模块
  5. import random
  6. import csv
  7. def get_phone():
  8. # 随机从列表中选择一个值
  9. nums = ["132", "130", "131", "133", "134", "135", "136", "137", "138", "139",
  10. "155", "157", "159"]
  11. # 随机从列表中选择一个元素 作为手机号的前3位。
  12. num = random.choice(nums)
  13. # 定义空的字符串
  14. p8 = ""
  15. # 生成8个随机数字
  16. for i in range(8):
  17. # 每次循环都生成一个随机数字
  18. n = random.randint(0, 9)
  19. # 将数字n跟 p8 进行拼接 因为是n是数字,将n转换位字符串进行拼接
  20. p8 = p8 + str(n)
  21. return num+p8
  22. def get_id():
  23. from common.file_handler import get_root_dir
  24. # 获取项目的根目录
  25. root_dir = get_root_dir()
  26. import os
  27. # 路径拼接
  28. csvfile = os.path.join(root_dir,'config/gb2260.csv')
  29. def get_code():
  30. """
  31. 主要是生成随机的前6位
  32. """
  33. # 创建一个空的列表
  34. all_code = []
  35. # 读取 gb2260.csv 文件
  36. with open(file=csvfile, encoding='utf8', mode='r') as f:
  37. # 使用csv 创建读取对象
  38. cr = csv.reader(f)
  39. for code in cr:
  40. # print(code, type(code),code[0])
  41. # 每次获取一个code 将code值存放在 all_code 中
  42. all_code.append(code[0])
  43. # 等文件读取完成之后 all_code中存放了所有的 code值
  44. # print(all_code)
  45. # 使用random 随机从 all_code 中选择一个
  46. cd = random.choice(all_code)
  47. # print(f"随机选中地区码:{cd}")
  48. return cd
  49. def get_ymd():
  50. """
  51. 主要是生成随机的4位年 2位月 2位 日
  52. """
  53. # 生成年月日
  54. # 生成年份
  55. y = random.randint(1950, 2020)
  56. # print(f"生成的年份:{y}")
  57. # 生成月份
  58. m = random.randint(1, 12)
  59. if m < 10:
  60. # 月份前面补0,将数字转换位字符串
  61. m = "0" + str(m)
  62. # print(f"生成的月份:{m}")
  63. # 生成天
  64. # 根据月份和年份
  65. # 1 当月份为 1,3,5,7,8,10,12 月,天数31天
  66. if int(m) in [1, 3, 5, 7, 8, 10, 12]:
  67. d = random.randint(1, 31)
  68. # 2,当月份为 4,6,9,11 月 天数是30天
  69. elif int(m) in [4, 6, 9, 11]:
  70. d = random.randint(1, 30)
  71. # 3.2月份分为平年和闰年, 如果是闰年的29天
  72. # 公元年分为4的倍数但非100的倍数 或者 为400的倍数
  73. elif (y % 4 == 0 and y % 100 != 0) or y % 400 == 0:
  74. d = random.randint(1, 29)
  75. # 以上都不符合,那就是平年的2月份
  76. else:
  77. d = random.randint(1, 28)
  78. # 如果d小于10 ,需要补0
  79. if d < 10:
  80. d = "0" + str(d)
  81. # print(f"生成的日期:{d}")
  82. return str(y) + str(m) + str(d)
  83. def get_last():
  84. """
  85. 生成身份证号的后4位
  86. """
  87. l3 = ""
  88. # 循环三次
  89. for i in range(3):
  90. # 每次都生成一个随机数字进行拼接
  91. l3 = l3 + str(random.randint(0, 9))
  92. # 最后一位有可能是X,也可能是数字
  93. l1 = random.choice([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "X"])
  94. # print(f"生成的最后四位: {l3}{l1}")
  95. return str(l3) + str(l1)
  96. code = get_code()
  97. ymd = get_ymd()
  98. last = get_last()
  99. return str(code)+str(ymd)+str(last)
  100. if __name__ == '__main__':
  101. phone = get_phone()
  102. user_id = get_id()
  103. print(user_id)

编写完成之后,测试无误,提交代码。

后面根据业务需要,有新的带有某些功能的代码 都可以放在common 包下。

创建test_cases 测试用例包

将所有的测试用例都放在test_cases 包下面。
我们目前的测试场景主要

  • 正常业务流程 test_business
  • 单接口的正常,异常场景 test_single_api

    安装依赖包

    因为每个项目使用虚拟环境都是独立,所以在新项目中需要安装 requests, pytest。 ```python pip install pytest

pip install requests

  1. <a name="wE9rx"></a>
  2. ## 编写测试代码
  3. <a name="wMSGJ"></a>
  4. ### 正常业务流程
  5. 代码中需要生成测试手机号,通过引用common 包下创建的代码。
  6. ```python
  7. from common.mytools import get_phone
  8. # 随机生成手机号码
  9. phone = get_phone()

进行上下游传参,我们将数据定义在字典中,如果执行多个文件,上下游传参的字典放在config 包下

jsondata = {
    "token":None,
    "goodsid":None,
}

image.png
编写测试用例,通过导入的方式将 手机号,jsondata 导入即可。

"""
下单业务主流程
"""


import requests
import random
from common.mytools import get_phone
from config.mydata import jsondata

# 随机生成手机号码
phone = get_phone()


def test_register():
    # 定义注册用户的地址
    url = "http://49.233.108.117:28019/api/v1/user/register"
    # 定义请求数据
    bodydata = {
        "loginName": str(phone),
        "password": "123456"
    }
    print(bodydata)
    # 发送请求  url= 表示请求地址, json= 请求数据为json格式
    r = requests.post(url=url, json=bodydata)
    # r 是整个服务器返回的结果
    print("状态码:", r.status_code)
    # 服务器返回结果
    print("结果:", r.json())

def test_login():
    url = "http://49.233.108.117:28019/api/v1/user/login"
    bodydata = {
        "loginName": str(phone),
        "passwordMd5": "E10ADC3949BA59ABBE56E057F20F883E"
    }
    print(bodydata)
    r = requests.post(url, json=bodydata)
    print(r.status_code)
    print(r.json())
    # 将token提取出来 放在 字典格式数据中  - 上游传参
    jsondata["token"] = r.json()["data"]

def test_search():
    url = "http://49.233.108.117:28019/api/v1/search"
    # 信息头数据  下游接口中使用到这个值
    headerdata = {
        "token": jsondata["token"]
    }

    # 请求参数
    querydata = {
        "keyword": "iphone"
    }
    # 发送get 请求 params get请求的参数, headers 请求头数据
    r = requests.get(url=url,params=querydata,headers=headerdata)
    print(r.status_code)
    print(r.json())
    # 所有的商品 存放在一个列表中
    all_goods = r.json()['data']['list']
    # 创建空的列表
    gids = []
    # 循环列表 拿到每一个商品
    for goods in all_goods:
        # 从商品中访问 goodsId
        gid = goods["goodsId"]
        print(gid)
        # 将商品id 放在列表中
        gids.append(gid)
    # 循环结束 可以看到所有的商品id
    print(gids)
    # 随机选择一个 作为参数
    jsondata["goodsid"] = random.choice(gids)

def test_add_cart():
    url = "http://49.233.108.117:28019/api/v1/shop-cart"
    # 信息头数据  下游接口中使用到这个值
    headerdata = {
        "token": jsondata["token"]
    }
    bodydata = {
        "goodsCount": random.randint(1,5),
        "goodsId": jsondata["goodsid"]
    }
    r = requests.post(url,json=bodydata,headers=headerdata)
    print(r.status_code)
    print(r.json())

执行,用例
image.png
用例通过之后,可以再次提交代码。

单接口测试

需要将数据保存到csvdata 目录中,csvdata 目录的操作最好也需封装一下。
在common包下 file_handler.py 文件中再新建函数

import os

def get_root_dir():
    """
    生成项目的根目录
    :return:
    """
    # 生成当前文件的绝对路径
    p1 = os.path.abspath(__file__)
    p2 = os.path.dirname(p1)
    p3 = os.path.dirname(p2)
    return p3

def get_csvdata_dir():
    root_dir = get_root_dir()
    # 路径拼接
    csvdata_dir = os.path.join(root_dir,'csvdata')
    # 如果路径不存在 那就创建
    if not os.path.exists(csvdata_dir):
        os.mkdir(csvdata_dir)

    return csvdata_dir



if __name__ == '__main__':
    root_dir = get_root_dir()
    print(f"项目根目录 {root_dir}")

    csvdata = get_csvdata_dir()
    print(f"csvdata目录路径: {csvdata}")

image.png

编写单接口测试代码

import pytest
import requests

tokens =["","e18de36f-d9ce-47e6-a2aa-1cf6508ec10","e18de36f-d9ce-47e6-a2aa-1cf6508ec10b","240d17fe-9a54-4d78-b73d-7600af599174"]
titles = ["","1234","1234567890"]
tabs=["ask","share","dev","job","","jk"]
contents = ["","xxxx"]
#存放所有的数据
all_case_data = []
for token in tokens:
    for title in titles:
        for tab in tabs:
            for content in contents:
                # 根据条件来生成对应的断言结果
                if token != "e18de36f-d9ce-47e6-a2aa-1cf6508ec10b":
                    errmsg = "错误的accessToken"
                elif len(title) == 0:
                    errmsg = "标题不能为空"
                elif len(title) < 10:
                    errmsg = "标题字数太多或太少"
                elif tab not in ['ask','share','dev','job']:
                    errmsg = '必须选择一个版块'
                elif len(content) == 0:
                    errmsg = '内容不可为空'
                # 如果数据都是正常的数据 发帖成功
                else:
                    errmsg = None


                # 每次循环,生成一条数据
                casedata = (token,title,tab,content,errmsg)
                # 将生成的数据放在all_case_data中
                all_case_data.append(casedata)




import csv
import os
from common.file_handler import get_csvdata_dir
csvdata = get_csvdata_dir()
topicfile = os.path.join(csvdata,'topic.csv')
# 创建文件
f = open(file=topicfile, mode='w', encoding='utf8', newline="")
cw = csv.writer(f)
# 写入表头
cw.writerow(["请求地址", "请求方法", "请求头信息", "请求体", "服务器返回状态码", "服务器返回结果"])

@pytest.mark.parametrize("token,title,tab,content,errmsg",all_case_data)
def test_create_topic(token,title,tab,content,errmsg):
    url = "http://47.100.175.62:3000/api/v1/topics"
    bodydata = {
        "accesstoken":token,
        "title":title,
        "tab":tab,
        "content":content
    }
    r = requests.post(url=url,json=bodydata)
    print(r.json())

    # 将执行的数据,服务器返回的结果保存到文件中。
    # 写入数据
    cw.writerow([url,"post","",bodydata,r.status_code, r.json()])

    # 添加断言  因为生成的数据包含正常场景
    if errmsg is not None:
        assert r.json()["error_msg"]  == errmsg

运行没有问题提交代码。
image.png

创建main文件

在项目的根目录创建main文件,每次做回归测试,或者冒烟测试,批量跑接口可以从main文件直接运行。

安装pytest-html

用来生成测试报告

pip install pytest-html

添加测试报告目录

所有的测试报告文件都放在reports 目录下,在common/file_handler.py 文件中添加生成测试报告文件目录的代码。

import os

def get_root_dir():
    """
    生成项目的根目录
    :return:
    """
    # 生成当前文件的绝对路径
    p1 = os.path.abspath(__file__)
    p2 = os.path.dirname(p1)
    p3 = os.path.dirname(p2)
    return p3

def get_csvdata_dir():
    root_dir = get_root_dir()
    # 路径拼接
    csvdata_dir = os.path.join(root_dir,'csvdata')
    # 如果路径不存在 那就创建
    if not os.path.exists(csvdata_dir):
        os.mkdir(csvdata_dir)

    return csvdata_dir


def get_reports_dir():
    root_dir = get_root_dir()
    reports = os.path.join(root_dir,'reports')
    if not os.path.exists(reports):
        os.mkdir(reports)

    return reports


if __name__ == '__main__':
    root_dir = get_root_dir()
    print(f"项目根目录 {root_dir}")

    csvdata = get_csvdata_dir()
    print(f"csvdata目录路径: {csvdata}")

    report = get_reports_dir()
    print(f"生成测试报告目录: {report}")

image.png

编写main文件

在main 文件中定义生成测试报告文件

import pytest
import os
import time  # 日期时间模块
from common.file_handler import get_reports_dir

reports = get_reports_dir()
#转换当前时间  %Y 年  %m 月  %d 日  %H 小时  %M 分钟  %S 秒
current_time = time.strftime("%Y_%m_%d_%H_%M_%S")
# html文件路径拼接
htmlfile = os.path.join(reports,f'report_{current_time}.html')

if __name__ == '__main__':
    # 通过调用pytest 内置的方法来运行所有的测试用例
    pytest.main(['test_cases',
                 f'--html={htmlfile}',
                 '--self-contained-html'])

运行,可以看到结果。
image.png