AirJD 焦点
AirJD

没有录音文件
00:00/00:00
加收藏

七牛如何做HTTP服务测试 by 许式伟

发布者 gopher   简介 Gopher
发布于 1430387805676  浏览 5410 关键词 Go, 自动化测试 
分享到

第1页

七牛如何做 

HTTP服务测试?

许式伟  2015-4-18 



第2页

HTTP服务测试 

• 单元测试 

– 某个独立子服务的测试 

• 集成测试 

– 整个集群对外业务API的测试 

• Stage环境  • Product环境 



第3页

怎么测? 

• 七牛早期做法 

– 实现服务逻辑(Service Implementation)  – 实现客户端SDK(Client Implementation)  – 基于客户端SDK写测试案例(Test Case) 

• 问题 

– 客户端SDK修改导致测试案例编不过  – 客户端SDK通常是使用方友好,而不是测试方友好  – 让服务端与客户端SDK耦合,容易过早陷入客户端SDK如何抽象

更合理的细节,而不能专注于测试服务逻辑本身 



第4页

换个角度 

• 直接基于协议测试呢? 

– 比如,基于 http.Client 类直接写测试案例 

• 问题 

– 代码相对冗长  – 业务逻辑表达不直观 

• 写一些辅助函数能够略为改观,不过会有逐步写测试专用SDK的倾向 



第5页

七牛当前做法 

• 引入 httptest DSL 文法  • 更接近基于 http.Client 写测试案例的思路 

– 但努力让代码更直白体现测试用意 



第6页

Hello, world! 



第7页

Quick start 



第8页

语法结构 

• 基于命令行文法 

command switch1 switch2 … arg1 arg2 … 

 

• 转义 

– 如果参数包含空格或其他特殊字符,则可以: 

• 用 \ 转义 

– 比如 '\ ' 表示 ' ' (空格),'\t' 表示 TAB 字符,等等 

• 用 '...' 或 "..." 包含 

– '...' 中不支持用 \ 转义,也不支持子命令,出现任何内容都当作普通字符 对待 



第9页

语法结构 

• 区别于Linux Shell的地方 

– 参数类型不只是字符串,有完整类型系统(支持且仅支持所有 json的数据类型) 

• string (如:"a"、application/json),在不引起歧义的情况下,可以 省略双引号 

• number (如:3.14159)  • boolean (如:true)  • array (如:["a", 200, {"b": 2}])  • dictionary/object (如:{"a": 1, "b": 2}) 

– 子命令相当于函数,返回任意类型的数据 

• 比如 `qiniu f2weae23e6c9f jg35fae526kbce` 返回一个 auth  object,用字符串无法表达 



第10页

http Request 的表达 

req    header    …  header    …  auth   body   



第11页

样例 

• 无授权的GET请求 

 req GET http://www.qiniu.com/ 

  • 带授权的POST请求 

 req POST http://foo.com/objects   auth `qiniu f2weae23e6c9f jg35fae526kbce`   body application/json '{     "a": "hello1", "b": 2   }' 



第12页

简写 

• 无授权的GET请求   get http://www.qiniu.com/ 

  • 带授权的POST请求 

 post http://foo.com/objects   auth `qiniu f2weae23e6c9f jg35fae526kbce`   json '{     "a": "hello1", "b": 2   }' 



第13页

http Response 匹配 

ret   header    …  header    …  body   



第14页

ret 指令 

• ret 

– 发起 http Request 请求,并将 http Response 存储到 $(resp)  变量中 

• ret  

– 等价于 

ret  match  $(resp.code) 



第15页

匹配(match) 

• 这几乎是这套 DSL 中最核心的概念 

– match   

• 要求  必须和  匹配  •  中不允许出现未绑定的变量  •  中允许存在未绑定的变量 

– 如果  中出现了已绑定的变量,则要求该变量必须匹配   中对应的值 

– 如果  中出现了未绑定的变量,则该变量会被赋值为   中对应的值 

– 匹配 

• 对于 number/string/boolean/array 类型 

– match A  B 意味着要求 A == B 

• 对于 object(dictionary) 类型 

– match A B 意味着 A 中出现的 item,在 B 中必须出现并且匹配 



第16页

例子 

• 例子1  match $(a.b) 1  match $(a.c) hello  结果 $(a) 的值为 {"b": 1, "c": "hello"} 

• 例子2  match $(a.b) 1  match $(a.b) 1   #可以匹配,因为$(a.b)的值的确为1  match $(a.b) 2   #失败,1和2不相等 

• 例子3  match '{"c": {"d": $(d)}}' '{"c": {"d": "hello", "e": "world"},  "f": 1}'  结果 $(d) 的值为 "hello" 



第17页

理解 http Response 匹配 

• ret  

– 等价于 

ret  match  $(resp.code) 

• header    … 

– 等价于 

match '[, , …]' $ (resp.header.) 

• body   

– 等价于 

match '[]' $(resp.header.Content-Type)  match  $(resp.body) 

 



第18页

断言 

• equal   

– 与 match 不同,,  中都不允许出现未绑 定的变量 

– 与 match 不同,equal 要求 ,  的值精确 相等 

• equalSet   

– Set 是指集合  – 与 equal 不同,equalSet 要求 ,  都是 

array,并且对 array 的元素进行排序后两者精确相等 



第19页

例子 

• 例子1 

get http://foo.com/objects/a325gea2kgfd  auth qiniutest  ret 200  equal '{"a": "hello1", "b": 2}' $(resp.body)  match '{"a": "hello1", "b": 2}' $(resp.body) 

• 例子2 

get http://foo.com/objects  auth qiniutest  ret 200  equalSet $(resp.body), '[ 

 {"a": "hello1", "b": 2},   {"a": "world2", "b": 4}  ]'   



第20页

测试环境 

• 如何让 stage 和 product 环境共享测试案例? 

– 测试环境参数化  – 也方便测试脚本入库(不入库 User/Password、AK/SK 这种敏感

信息) 



第21页

host 指令 

• 服务地址参数化 

 

host foo.com 127.0.0.1:8888 

 

get http://foo.com/objects/a325gea2kgfd  auth qiniutest  ret 200  json '{"a": "hello1", "b": 2}' 



第22页

Host、AK/SK 参数化 

match $(testenv) `env QiniuTestEnv`  match $(env) `envdecode QiniuTestEnv_$(testenv)`    host foo.com $(env.FooHost)  auth qiniutest `qiniu $(env.AK) $(env.SK)`    post http://foo.com/objects  auth qiniutest  json '{"a": "hello1", "b": 2}'  ret 200  json '{"id": $(id1)}'    get http://foo.com/objects/$(id1)  auth qiniutest  ret 200  json '{"a": "hello1", "b": 2}' 



第23页

env、envdecode 指令 

• env  

– 取环境变量  对应的值 

• decode  

– 将一个 json 字符串 decode 为对象(object) 

• envdecode  

– 取环境变量  对应的值,并且把它当做 json 字符串  decode 为一个对象(object) 

– 等价于 

match $(encodedval) `env `  decode $(encodedval) 



第24页

配置测试环境 

• 配置 stage、product 环境 

  export QiniuTestEnv_stage='{ 

 "FooHost": "192.168.1.10:8888",   "AK": "...",   "SK": "..."  }' 

 

export QiniuTestEnv_product='{   "FooHost": "foo.com",   "AK": "...",   "SK": "..." 

}'   



第25页

执行测试案例 

• 测试 stage 环境 

QiniuTestEnv=stage qiniutest ./testfoo.qtf 

  • 测试 product 环境 

QiniuTestEnv=product qiniutest ./testfoo.qtf 



第26页

未覆盖的内容… 

• echo/println – 打印变量内容(调试)  • auth – 授权的详细解剖  • base64 – 对字符串 base64 encode/decode  • case/setUp/tearDown – 如何定义多个案例  • … 



第27页

Q & A 

 

 



@许式伟 

  @七牛云存储 



支持文件格式:*.pdf
上传最后阶段需要进行在线转换,可能需要1~2分钟,请耐心等待。