编写测试用例

HttpRunner v3.x支持pytest,YAML和JSON三种测试用例格式。强烈建议以pytest格式而不是以前的YAML/JSON格式编写和维护测试用例。

格式关系如下图所示:


avatar

记录并生成测试用例

如果SUT(被测系统)准备就绪,最有效的方法是先捕获HTTP流量,然后使用HAR文件生成测试用例。
请参阅 记录并生成测试用例 获取更多详细信息。然后基于生成的pytest测试用例,您可以根
据需要进行一些调整,因此您需要了解测试用例格式的详细信息。

连锁会话

HttpRunner v3.x最令人敬畏的功能之一是chain call,您无需记住任何测试用例格式的详细信息,并且在IDE中编写测试用例时就可以智能完成。

avatar
avatar

测试用例结构

每个测试用例都是HttpRunner的子类,并且必须具有configteststeps两个类属性。

config

配置测试用例级别的设置,每个测试用例都应该有一个 config 部分。其中包括base_urlverifyvariablesexport

  • name(required)

    指定测试用例名称。这将显示在执行日志和测试报告中。

  • base_url(optional)

    指定被测系统的通用模式和主机部分,例如https://postman-echo.com。如果base_url指定,则teststep中的url只能设置相对路径部分。如果要在不同的SUT环境之间切换,这将特别有用。

  • variables(optional)

    指定测试用例的公共变量。每个测试步骤都可以引用未在步骤变量中设置的配置变量。换句话说,步骤变量比配置变量具有更高的优先级。

  • verify(optional)

    指定是否验证服务器的TLS证书。如果我们想记录测试用例执行的HTTP流量,这将特别有用。因为如果没有设置verify或将其设置为True,则会发生SSLError。

    SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate in certificate chain (_ssl.c:1076)'))

  • export(optional)

    指定导出的测试用例会话变量。将每个测试用例视为一个黑盒,config的 variables 是输入部分,而config的 export是输出部分。
    特别是,当一个测试用例在另一个测试用例的步骤中被引用,并且将被提取一些会话变量以在随后的测试步骤中使用时,则提取的会话变量应在配置export部分中进行配置。

teststeps

每个测试用例应具有一个或多个有序的测试步骤(List[Step]),每个步骤对应于一个API请求或另一个测试用例引用调用。
此外,variables(变量) / extract(提取) / validate(验证) / hooks机制支持,可制作十分复杂的测试方案。

avatar

注意:为了简化起见,不推荐使用HttpRunner v2.x中的API概念。您可以将API视为只有一个请求步骤的测试用例。

RunRequest(name)

RunRequest 在一个步骤中用于向API发出请求,并对响应进行一些提取或验证。
name 参数用于指定测试步骤名称,该名称将显示在执行日志和测试报告中。

  • .with_variables

    指定测试步骤变量。每个步骤的变量都是独立的,因此,如果要在多个步骤中共享变量,
    则应在配置变量中定义变量。此外,步骤变量将覆盖配置变量中具有相同名称的变量。
  • .method(url)

    指定HTTP方法和被测系统的URL。这些对应于method和url参数requests.request
    如果在config中设置了base_url,则url只能设置相对路径部分。例如:post("/index")
  • .with_params

    指定请求网址的查询字符串。与requests.request中的params参数相对应。
  • .with_headers

    指定HTTP的请求头。与requests.request中的headers参数相对应。
  • .with_cookies

    指定HTTP请求cookie。与requests.request中的cookies参数相对应。
  • .with_data

    指定HTTP请求正文。与requests.request中的data参数相对应。
  • .with_json

    指定HTTP请求正文是json格式。与requests.request中的json参数相对应。
  • extract

    .WITH_JMESPATH
    使用jmespath提取JSON响应主体。

    with_jmespath(jmes_path: Text, var_name: Text)

    • jmes_path:jmespath表达式,有关更多详细信息,请参考《JMESPath教程》
    • var_name:存储提取值的变量名,可以在后续测试步骤中引用它。
  • validate

    .ASSERT_XXX
    使用jmespath提取JSON响应主体并使用期望值进行验证。

    assert_XXX(jmes_path: Text, expected_value: Any, message: Text = "")

    • jmes_path:jmespath表达式,有关更多详细信息,请参考《JMESPath教程》
    • expected_value:指定的期望值,变量或函数引用也可以在此处使用。
    • message(可选):用于指示断言错误原因。

RunTestCase(name)

RunTestCase 在一个步骤中用于引用另一个测试用例调用。
name 参数用于指定测试步骤名称,该名称将显示在执行日志和测试报告中。

  • .with_variables

    RunRequest中的.with_variables相同。
  • .call

    引用指定的测试用例类
  • .export

    从引用的测试用例中导出指定会话变量名称,可以在后续的测试步骤中引用导出的变量。

示例1

from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase


class TestCaseRequestWithFunctions(HttpRunner):
    config = (
        Config("request methods testcase with functions")
        .variables(
            **{
                "foo1": "config_bar1",
                "foo2": "config_bar2",
                "expect_foo1": "config_bar1",
                "expect_foo2": "config_bar2",
            }
        )
        .base_url("https://postman-echo.com")
        .verify(False)
        .export(*["foo3"])
    )

    teststeps = [
        Step(
            RunRequest("get with params")
            .with_variables(
                **{"foo1": "bar11", "foo2": "bar21", "sum_v": "${sum_two(1, 2)}"}
            )
            .get("/get")
            .with_params(**{"foo1": "$foo1", "foo2": "$foo2", "sum_v": "$sum_v"})
            .with_headers(**{"User-Agent": "HttpRunner/${get_httprunner_version()}"})
            .extract()
            .with_jmespath("body.args.foo2", "foo3")
            .validate()
            .assert_equal("status_code", 200)
            .assert_equal("body.args.foo1", "bar11")
            .assert_equal("body.args.sum_v", "3")
            .assert_equal("body.args.foo2", "bar21")
        ),
        Step(
            RunRequest("post form data")
            .with_variables(**{"foo2": "bar23"})
            .post("/post")
            .with_headers(
                **{
                    "User-Agent": "HttpRunner/${get_httprunner_version()}",
                    "Content-Type": "application/x-www-form-urlencoded",
                }
            )
            .with_data("foo1=$foo1&foo2=$foo2&foo3=$foo3")
            .validate()
            .assert_equal("status_code", 200)
            .assert_equal("body.form.foo1", "$expect_foo1")
            .assert_equal("body.form.foo2", "bar23")
            .assert_equal("body.form.foo3", "bar21")
        ),
    ]


if __name__ == "__main__":
    TestCaseRequestWithFunctions().test_start()

示例2

import os
import sys

sys.path.insert(0, os.getcwd())

from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase

from examples.postman_echo.request_methods.request_with_functions_test import (
    TestCaseRequestWithFunctions as RequestWithFunctions,
)


class TestCaseRequestWithTestcaseReference(HttpRunner):
    config = (
        Config("request methods testcase: reference testcase")
        .variables(
            **{
                "foo1": "testsuite_config_bar1",
                "expect_foo1": "testsuite_config_bar1",
                "expect_foo2": "config_bar2",
            }
        )
        .base_url("https://postman-echo.com")
        .verify(False)
    )

    teststeps = [
        Step(
            RunTestCase("request with functions")
            .with_variables(
                **{"foo1": "testcase_ref_bar1", "expect_foo1": "testcase_ref_bar1"}
            )
            .call(RequestWithFunctions)
            .export(*["foo3"])
        ),
        Step(
            RunRequest("post form data")
            .with_variables(**{"foo1": "bar1"})
            .post("/post")
            .with_headers(
                **{
                    "User-Agent": "HttpRunner/${get_httprunner_version()}",
                    "Content-Type": "application/x-www-form-urlencoded",
                }
            )
            .with_data("foo1=$foo1&foo2=$foo3")
            .validate()
            .assert_equal("status_code", 200)
            .assert_equal("body.form.foo1", "bar1")
            .assert_equal("body.form.foo2", "bar21")
        ),
    ]


if __name__ == "__main__":
    TestCaseRequestWithTestcaseReference().test_start()
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351

推荐阅读更多精彩内容