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