HttpRunner 从 v4.0 开始新增支持 WebSocket 协议。
本文将结合案例初步介绍使用 HttpRunner v4.0 测试 WebSocket 的方法,欢迎大家多多实践,后续我们将基于大家的反馈进行迭代优化。
在 HttpRunner v4.0 中,当前针对 WebSocket 支持了如下能力:
实践出真知,首先我们来看一个完整的测试用例。
该测试用例使用的被测服务是一个类似于 httpbin 的回显服务,协议类型是 WebSocket 协议:ws://echo.websocket.events。无论客户端发送了什么消息,服务端都会原封不动地返回,具体效果可以参考链接。
测试用例总共包含了如下的 10 个步骤:
status_code 和响应头 headers.Connection 的内容进行断言body 中包含的字符串内容进行断言,超时时间设为 5 秒{"foo1": "${gen_random_string($n)}", "foo2": "${max($a, $b)}"},注意该消息内容中用到了 HttpRunner 的内置函数 gen_random_string 和 max
varFoo1,随后再对 body.foo1 的长度和 body.foo2 的值进行断言varFoo1,并对服务端返回的消息进行读取,并且对 body 的长度进行断言上述测试用例对应的脚本形式如下:
config:
  name: run request with WebSocket protocol
  base_url: ws://echo.websocket.events
  variables:
    a: 12.3
    b: 3.45
    file: "./demo_file_load_ws_message.txt"
    n: 5
teststeps:
- name: open connection
  websocket:
    type: open
    url: "/"
    headers:
      User-Agent: HttpRunnerPlus
  validate:
  - check: status_code
    assert: equals
    expect: 101
    msg: check open status code
  - check: headers.Connection
    assert: equals
    expect: Upgrade
    msg: check headers
- name: ping pong test
  websocket:
    type: ping
    url: "/"
    timeout: 5000
- name: read sponsor info
  websocket:
    type: r
    url: "/"
    timeout: 5000
  validate:
  - check: body
    assert: contains
    expect: Lob.com
    msg: check sponsor message
- name: write json
  websocket:
    type: w
    url: "/"
    text:
      foo1: "${gen_random_string($n)}"
      foo2: "${max($a, $b)}"
- name: read json
  websocket:
    type: r
    url: "/"
  extract:
    varFoo1: body.foo1
  validate:
  - check: body.foo1
    assert: length_equals
    expect: 5
    msg: check json foo1
  - check: body.foo2
    assert: equals
    expect: 12.3
    msg: check json foo2
- name: write and read text
  websocket:
    type: wr
    url: "/"
    text: "$varFoo1"
  validate:
  - check: body
    assert: length_equals
    expect: 5
    msg: check length equal
- name: write and read binary file
  websocket:
    type: wr
    url: "/"
    binary: "${load_ws_message($file)}"
- name: write something redundant
  websocket:
    type: w
    url: "/"
    text: have a nice day!
- name: write something redundant
  websocket:
    type: w
    url: "/"
    text: balabala ...
- name: close connection
  websocket:
    type: close
    url: "/"
    close_status: 1000
    timeout: 30000
  validate:
  - check: status_code
    assert: equals
    expect: 1000
    msg: check close status code
 
需要说明的是,该测试用例中的示例文件需要由用户自己来指定,文件路径填写有效的绝对路径或相对路径即可,这里的示例文件  demo_file_load_ws_message.txt  只是以 txt 文件为例,实际上该文件可以为任意类型,最终导入之后都为二进制类型。
除了 YAML/JSON 格式外,我们也可以采用 gotest 的方式编写用例,形式如下:
package tests
import (
   "testing"
   "github.com/httprunner/httprunner/v4/hrp"
)
func TestWebSocketProtocol(t *testing.T) {
   testcase := &hrp.TestCase{
      Config: hrp.NewConfig("run request with WebSocket protocol").
         SetBaseURL("ws://echo.websocket.events").
         WithVariables(map[string]interface{}{
            "n":    5,
            "a":    12.3,
            "b":    3.45,
            "file": "./demo_file_load_ws_message.txt",
         }),
      TestSteps: []hrp.IStep{
         hrp.NewStep("open connection").
            WebSocket().
            OpenConnection("/").
            WithHeaders(map[string]string{"User-Agent": "HttpRunnerPlus"}).
            Validate().
            AssertEqual("status_code", 101, "check open status code").
            AssertEqual("headers.Connection", "Upgrade", "check headers"),
         hrp.NewStep("ping pong test").
            WebSocket().
            PingPong("/").
            WithTimeout(5000),
         hrp.NewStep("read sponsor info").
            WebSocket().
            Read("/").
            WithTimeout(5000).
            Validate().
            AssertContains("body", "Lob.com", "check sponsor message"),
         hrp.NewStep("write json").
            WebSocket().
            Write("/").
            WithTextMessage(map[string]interface{}{"foo1": "${gen_random_string($n)}", "foo2": "${max($a, $b)}"}),
         hrp.NewStep("read json").
            WebSocket().
            Read("/").
            Extract().
            WithJmesPath("body.foo1", "varFoo1").
            Validate().
            AssertLengthEqual("body.foo1", 5, "check json foo1").
            AssertEqual("body.foo2", 12.3, "check json foo2"),
         hrp.NewStep("write and read text").
            WebSocket().
            WriteAndRead("/").
            WithTextMessage("$varFoo1").
            Validate().
            AssertLengthEqual("body", 5, "check length equal"),
         hrp.NewStep("write and read binary file").
            WebSocket().
            WriteAndRead("/").
            WithBinaryMessage("${load_ws_message($file)}"),
         hrp.NewStep("write something redundant").
            WebSocket().
            Write("/").
            WithTextMessage("have a nice day!"),
         hrp.NewStep("write something redundant").
            WebSocket().
            Write("/").
            WithTextMessage("balabala ..."),
         hrp.NewStep("close connection").
            WebSocket().
            CloseConnection("/").
            WithTimeout(30000).
            WithCloseStatus(1000).
            Validate().
            AssertEqual("status_code", 1000, "check close status code"),
      },
   }
   // run testcase
   err = hrp.NewRunner(t).Run(testcase)
   if err != nil {
      t.Fatalf("run testcase error: %v", err)
   }
}
 
可以看出,WebSocket 协议的脚本形式与之前 HTTP(S) 协议的格式基本一致。如果大家使用 gotest 编写脚本,也可以借助「链式调用」获得方法提示。
WebSocket 不同于 HTTP 协议,不再使用 GET/POST/PUT/etc. 请求方法,而是包含 open/close/ping/w/r/wr 6 种操作类型。
下面对这 6 种操作类型进行介绍:
load_ws_message 来导入消息内容。由于写消息的过程是客户端向服务端的单向通信过程,不存在接收响应的过程,因此也不支持响应的参数提取和结果断言需要注意的是,6 种操作类型会存在一些限制差异,可查看如下对比表格:
| 操作类型 | open | ping | w | r | wr | close | 
|---|---|---|---|---|---|---|
| 支持设置超时时间 | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | 
| 支持设置请求参数和请求头 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | 
| 是否为阻塞操作 | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | 
| 支持发送消息 | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | 
| 支持设置状态码 | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | 
| 支持参数提取和结果断言 | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | 
另外,在测试 WebSocket 协议的时候还需注意如下事项:
load_ws_message 从本地文件导入的方式来加载二进制消息,因为通过 JSON/YAML 脚本的方式无法直接指定二进制消息的内容(采用 go test 的方式来编写测试用例时,也可以通过 bytes.Buffer 的方式来指定二进制消息内容)hrp har2case 命令还不支持将包含 WebSocket 请求的 har 包转换为 JSON/YAML 测试用例,HttpRunner v4.0 之后的版本中将会对该特性进行支持本文作者:卜卜星,HttpRunner 核心开发者,贡献了本文介绍的 WebSocket 协议测试能力
HttpRunner 项目官网: https://httprunner.com/
如果 HttpRunner 对你有过帮助,麻烦帮忙给个 ⭐️star⭐️ 鼓励下吧
https://github.com/httprunner/httprunner