断言作业答案

  1. # 接口地址 http://49.233.108.117:28019/swagger-ui.html#/
  2. import requests
  3. # 导入已经编写好的生成手机号码的函数
  4. from common.utils import generate_phone
  5. import random
  6. base_url = "http://49.233.108.117:28019"
  7. # 注册,登录使用的是同一个手机号码 定义变量
  8. phone = generate_phone()
  9. testdata ={
  10. "token":"", # token,
  11. "gid": 0 # 商品id
  12. }
  13. def test_user_register():
  14. """
  15. 用户注册
  16. :return:
  17. """
  18. url = base_url+"/api/v1/user/register"
  19. body_data = {
  20. "loginName": phone,
  21. "password": "123456"
  22. }
  23. # 发送请求
  24. r = requests.post(url,json=body_data)
  25. print(r.json())
  26. # 添加断言
  27. # code为200
  28. assert r.status_code == 200
  29. assert r.json()['resultCode'] == 200
  30. # msg
  31. assert r.json()['message'] == 'SUCCESS'
  32. def test_user_login():
  33. """
  34. 用户登录。 登录使用的手机号跟注册是同一个号码
  35. :return:
  36. """
  37. url = base_url+"/api/v1/user/login"
  38. body_data = {
  39. "loginName": phone,
  40. "passwordMd5": "E10ADC3949BA59ABBE56E057F20F883E"
  41. }
  42. r = requests.post(url,json=body_data)
  43. print(r.json())
  44. # 断言
  45. # code 断言
  46. assert r.status_code == 200
  47. assert r.json()['resultCode'] == 200
  48. # msg
  49. assert r.json()['message'] == 'SUCCESS'
  50. # 提取变量值 token
  51. testdata["token"] = r.json()["data"]
  52. def test_search():
  53. """
  54. 搜索商品 需要token
  55. :return:
  56. """
  57. url = base_url+'/api/v1/search'
  58. query_data = {
  59. "keyword":"iphone"
  60. }
  61. header={
  62. "token":testdata["token"]
  63. }
  64. # 搜索条件为 iphone
  65. r = requests.get(url,params=query_data,headers=header)
  66. # 断言
  67. # 所有搜索结果中都包含iphone
  68. # 拿到所有的商品信息
  69. allgoods = r.json()["data"]["list"]
  70. # 循环所有商品的时候
  71. for good in allgoods:
  72. # 拿到每个商品的名称
  73. goodname = good["goodsName"]
  74. print('商品的名称',goodname) # 因为返回结果中有大小写字母混合的场景。使用字符串转换大写方法
  75. assert 'iphone'.upper() in goodname.upper() # upper 将字符串转为大写
  76. # 将价格大于 5500 的商品id 随机抽取一个作为变量
  77. # 将所有价格大于5500 的商品id放在一个列表中
  78. gids = []
  79. for good in allgoods:
  80. if good["sellingPrice"] > 5500:
  81. gids.append(good["goodsId"])
  82. print("所有商品价格大于5500的商品id",gids)
  83. testdata["gid"] = random.choice(gids)
  84. def test_add_cart():
  85. """
  86. 添加购物车
  87. :return:
  88. """
  89. url = base_url+"/api/v1/shop-cart"
  90. body_data = {
  91. "goodsCount": 1,
  92. "goodsId": testdata["gid"] # 商品id
  93. }
  94. header = {
  95. "token":testdata["token"]
  96. }
  97. r = requests.post(url,json=body_data,headers=header)
  98. print(r.json())
  99. # 断言 添加成功
  100. assert r.status_code == 200
  101. assert r.json()['resultCode'] == 200
  102. # msg
  103. assert r.json()['message'] == 'SUCCESS'

参数化

在接口测试,测试单接口时,需要针对接口的 不同场景来进行测试。场景有正常场景,异常场景,不同的场景测试的数据不一样,结果也不一样。·比如登录接口,除了使用正确的用户名和密码进行登录测试,错误的用户名和密码也需要测试。测试数据比较多,所以可以将将数据保存在文件中进行测试 。

pytest 参数的基本样例

import pytest

@pytest.mark.parametrize("n",[0,10,20])
def test_n(n):
    print("n的值",n)

上面代码运行的时候, 会自动生成3个测试用例。
image.png

  • @pytest.mark.parametrize 这个是固定写法 表示参数化,
  • “n” n可以随意命名。 但是要跟下面 测试用例中的参数 n保持一致

image.png

  • [0,10,20] 这个是测试数据,必须将测试数据放在一个list中,有多少条数据,执行的时候会自动执行多少次,执行的时候会从list中去取每一条数据。

多个参数进行参数化

测试接口的时候,请求的数据有多个字段。也可以进行参数化。
将测试数据放到 列表中。

# 测试登录,需要用户名和密码两个参数
@pytest.mark.parametrize("name,passwd",[("xiaming","123456"),("","123456"),("xiaoming",""),("","")])
def test_login(name,passwd):
    print("用户名",name )
    print('密码',passwd)

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


在进行测试数据参数化的时候,因为数据直接写在代码中,数据比较多的时候。也可以将数据直接定义在函数外边,直接运行。

import pytest
data = [
    ("zhangsan","123456"),
    ("lisi","123456"),
    ("wangwu","123456")
]

@pytest.mark.parametrize("username,passwd",data)
def test_login_data(username,passwd):
    print("用户名",username)
    print("密码",passwd)

执行,可以看到执行的效果。
image.png

接口中应用

image.png
在cnode 社区 主题首页接口测试中。
测试点:

  1. tab的值 分别可以是 ask, share ,job, good

当tab的值分别为这四种中的一种时,服务器返回的结果所有内容的标题跟上面选择的保持一致。

  1. limit 最大值为 50,最小值为1,超过50按照50计算

pytest 参数化 - 图6

针对limit的测试点 编写测试代码


import requests
import pytest
limitdata = [
    (1,1),  # 当limit值为1 返回结果中只有1条数据
    (50,50),  # 当limit值为50 返回结果中只有50条数据
    (51,50), # 当limit值为51 返回结果中只有50条数据
]

@pytest.mark.parametrize("limit,expect_len",limitdata)
def test_topic_index(limit,expect_len):
    print(f'当limit字段值为{limit}时,期望结果为服务器返回结果数据长度为{expect_len}')
    url = "http://47.100.175.62:3000/api/v1/topics"
    querydata = {
        "limit": limit
    }
    r = requests.get(url,params=querydata)
    print(r.json())
    # 添加断言,不同请求数据返回结果不同
    assert len(r.json()["data"]) == expect_len

执行结果
image.png
针对 tab 值设置测试用例


import requests
import pytest

tabdata = [
    "ask",
    "good",
    "job",
    "share"
]
@pytest.mark.parametrize("tab",tabdata)
def test_page_tabdata(tab):
    print(f'当tab字段值为{tab}时,期望结果为服务器返回结果tab值都是{tab}')
    url = "http://47.100.175.62:3000/api/v1/topics"
    querydata = {
        "tab": tab
    }
    r = requests.get(url, params=querydata)
    print(r.json())
    # 添加断言,不同请求数据返回结果不同
    for topic in r.json()["data"]:
        assert topic["tab"] == tab

上面的代码 将 tab 和 limit 分开来测试,在实际的业务场景中,有些接口要测试的字段笔记多,拆开来做,用例的量也会很大。所以在测试过程中最好放在一个里面,统一来测试。

  1. 当 tab 的值 为 ask的时候, limit 可以取的值为 1,50,51
  2. 当 tab 的值 为 share的时候, limit 可以取的值为 1,50,51
  3. 当 tab 的值 为 job的时候, limit 可以取的值为 1,50,51
  4. 当 tab 的值 为 good的时候, limit 可以取的值为 1,50,51

就有不同的排列组合,如果人工来测试,这个工作量是比较大的,但是如果用自动化代码来测,这个就是分分钟就可以搞定的事情。

tab_v = ["ask","share","job","good"]
limit_v = [1,50,51]
# 将数据排列组合一下
test_tab_limit_data = []
for tab in tab_v:
    for limit in limit_v:
        print(f'当 tab 的值 为 {tab}的时候, limit 可以取的值为 {limit}')
        test_tab_limit_data.append((tab,limit)) # (tab,limit) 表示元组

print("生成测试数据为",test_tab_limit_data)

使用for循环 生成可能测试数据。image.png
上面的代码只是生成测试数据中请求参数的不同组合,在做自动化的时候,还需要生成对应的断言结果。
比如 当tab值为 ask 断言 返回结果中tab值为 ask,当limit的值为1的时候,断言结果为1。
期望最终生成的数据

testdata = [
    ("ask",1,"ask",1), # 输入 ask,1 期望结果 ask 1
]

生成测试数据

编写代码 生成多条测试数据,并根据条件添加对应的断言。

tab_v = ["ask","share","job","good"]
limit_v = [1,50,51]
# 将数据排列组合一下
test_tab_limit_data = []
for tab in tab_v:
    for limit in limit_v:
        expect_tab = tab  # 期望的tab值
        # 需要注意: 当limit = 51, 期望的结果是50
        if limit >= 51:  # limit为51 时候
            expect_limit = 50  # 设置为50
        else:  # 其他场景
            expect_limit = limit  # 期望的limit 值
        test_tab_limit_data.append((tab,limit,expect_tab,expect_limit)) #  (tab,limit) 元组数据类型
        print(f'当tab的值为{tab}的时候, limit值为 {limit}, 期望结果中的tab值{expect_tab},期望结果中的limit{expect_limit}')
print("生成测试数据为",test_tab_limit_data)

上面的代码中 可以将所有的测试场景数据生成。下一步利用测试数据来进行自动化测试。

tab_v = ["ask","share","job","good"]
limit_v = [1,50,51]
# 将数据排列组合一下
test_tab_limit_data = []
for tab in tab_v:
    for limit in limit_v:

        expect_tab = tab  # 期望的tab值
        # 需要注意: 当limit = 51, 期望的结果是50
        if limit >= 51:  # limit为51 时候
            expect_limit = 50  # 设置为50
        else:  # 其他场景
            expect_limit = limit  # 期望的limit 值
        test_tab_limit_data.append((tab,limit,expect_tab,expect_limit)) #  (tab,limit) 元组数据类型
        print(f'当tab的值为{tab}的时候, limit值为 {limit}, 期望结果中的tab值{expect_tab},期望结果中的limit{expect_limit}')

@pytest.mark.parametrize("tab,limit,expect_tab,expect_limit",test_tab_limit_data)
def test_home_page(tab,limit,expect_tab,expect_limit):
    url = "http://47.100.175.62:3000/api/v1/topics"
    querydata = {
        "tab":tab,
        "limit":limit
    }
    r = requests.get(url,params=querydata)

    #断言tab值
    for topic in r.json()["data"]:
        assert topic["tab"] == expect_tab

    # 断言limit
    assert len(r.json()["data"]) == expect_limit

执行,可以看到执行结果
image.png
并且,还发现其中有三组数据 有问题。
经过分析: 发现系统中没有精华话题,所有这三个用例失败,通过这样的方式也可以发现被测系统的数据不完整。

csv数据

上面的代码可以生成对应的数据文件,如果你在公司没有使用Python做接口自动化,也可以使用Python代码生成测试数据保存到csv文件中,也可以使用其他工具做自动化,比如Postman,JMeter。

tab_v = ["ask","share","job","good"]
limit_v = [1,50,51]
# 将数据排列组合一下
test_tab_limit_data = []
for tab in tab_v:
    for limit in limit_v:

        expect_tab = tab  # 期望的tab值
        # 需要注意: 当limit = 51, 期望的结果是50
        if limit >= 51:  # limit为51 时候
            expect_limit = 50  # 设置为50
        else:  # 其他场景
            expect_limit = limit  # 期望的limit 值
        test_tab_limit_data.append((tab,limit,expect_tab,expect_limit)) #  (tab,limit) 元组数据类型
        print(f'当tab的值为{tab}的时候, limit值为 {limit}, 期望结果中的tab值{expect_tab},期望结果中的limit')
print("生成测试数据为",test_tab_limit_data)


# 写入到 csv 文件中
import csv
with open('home_topics.csv',mode='w',encoding='utf8',newline='') as file:
    w = csv.writer(file)
    # 写入标题
    w.writerow(["tab","limit","expect_tab","expect_limit"])
    # 写入数据
    for line in test_tab_limit_data:
        #每一种测试场景写入一行数据
        w.writerow(line)

运行成功之后,可以看到在项目根目录下生成对用 csv文件。
image.png
将项目整理合理的规划一下, 生成的测试数据可以放在专门的文件中。

添加common/datatool.py

在做参数化测试的时候,生成对应的测试数据方法 都放在 datatool.py文件中。
对应的文件目录
image.png

"""
针对不同的接口 生成对应的测试数据
提供测试数据
"""

def home_page_data():
    tab_v = ["ask", "share", "job", "good"]
    limit_v = [1, 50, 51]
    # 将数据排列组合一下
    test_tab_limit_data = []
    for tab in tab_v:
        for limit in limit_v:

            expect_tab = tab  # 期望的tab值
            # 需要注意: 当limit = 51, 期望的结果是50
            if limit >= 51:  # limit为51 时候
                expect_limit = 50  # 设置为50
            else:  # 其他场景
                expect_limit = limit  # 期望的limit 值
            test_tab_limit_data.append((tab, limit, expect_tab, expect_limit))  # (tab,limit) 元组数据类型
            # print(f'当tab的值为{tab}的时候, limit值为 {limit}, 期望结果中的tab值{expect_tab},期望结果中的limit')
    # print("生成测试数据为", test_tab_limit_data)
    return test_tab_limit_data

在测试用例直接通过调用 函数生成的数据来进行测试

"""
定义所有的单接口测试
"""
import pytest
import requests
from common.datatool import home_page_data

home_data = home_page_data()

@pytest.mark.parametrize("tab,limit,expect_tab,expect_limit",home_data)
def test_home_page(tab,limit,expect_tab,expect_limit):
    url = "http://47.100.175.62:3000/api/v1/topics"
    querydata = {
        "tab":tab,
        "limit":limit
    }
    r = requests.get(url,params=querydata)

    #断言tab值
    for topic in r.json()["data"]:
        assert topic["tab"] == expect_tab

    # 断言limit
    assert len(r.json()["data"]) == expect_limit

作业

测试新建主题的不同场景。

def create_topic_data():
    pass
def test_create_topics():
    pass

作业分析

分析每个参数可能的场景
pytest 参数化 - 图12
不同的数据 需要不同相应结果。

前提 条件 成功/失败 检查点
accesstoken 值为”” 失败 errormsg:错误的accesstoken
accesstoken 值为错误 失败 errormsg:错误的accesstoken
accesstoken值正常 title空 失败 errormsg:标题不能为空
accesstoken值正常 title值正常 ask 为空 失败 errormsg:必须选择一个版块
accesstoken值正常 title值正常 tab值正常 content为空 失败 errormsg:内容不可为空
所有值都符合条件 成功 success:true

生成对应的测试数据。


def create_topic_data():
    tokens = ["","xxxxxxyyyy","e18de36f-d9ce-47e6-a2aa-1cf6508ec10b"]
    titles = ["","helloworld"]
    tabs = ["ask","share","job","dev",""]
    contents = ["","helloworld"]
    testdata = []
    # 组合数据
    for token in tokens:
        for title in titles:
            for tab in tabs:
                for content in contents:
                    # 设置对应的断言
                    expect_success = False
                    if not token == "e18de36f-d9ce-47e6-a2aa-1cf6508ec10b":
                        expect_err_msg = "错误的accessToken"
                    elif title == "":
                        expect_err_msg ="标题不能为空"
                    elif tab == "":
                        expect_err_msg = "必须选择一个版块"
                    elif content == "":
                        expect_err_msg = "内容不可为空"
                    else:
                        #只有当发帖成功的时候 success 的结果才会 True
                        expect_success = True
                        # 发帖成功的时候,返回结果的数据类型跟失败的类型不一样,只对success 进行断言即可
                        expect_err_msg = ""

                    testdata.append((token,title,tab,content,expect_success,expect_err_msg))

    return testdata


if __name__ == '__main__':
    data = create_topic_data()
    print(data)

编写测试用例

topic_data=create_topic_data()
#数据的格式为 (token,title,tab,content,expect_success,expect_err_msg)

@pytest.mark.parametrize('token,title,tab,content,expect_success,expect_err_msg',topic_data)
def test_create_topics(token,title,tab,content,expect_success,expect_err_msg):
    url = "http://47.100.175.62:3000/api/v1/topics"
    body_data = {
        "accesstoken":token,
        "title":title,
        "tab": tab,
        "content":content
    }
    r = requests.post(url,json=body_data)
    # 进行断言
    # 1. success 结果断言
    assert r.json()["success"] == expect_success
    # 只有当 发帖失败的时候才会对错误提示信息进行断言
    if expect_success == False:
        assert r.json()["error_msg"] == expect_err_msg

运行接口。
可以发现接口中 还有两个 bug
image.png

视频

python 数据驱动.mp4 (275.45MB)