摘要:RestFixture 是 Fitnesse 的一个测试 REST 服务的插件,用于调用标准的 http GET/POST 等请求方法,并可以用 XPath 语法和 Javascript 语法检验 http 响应。本文介绍安装运行 RestFixture 的步骤,并给出测试用例示例。
首先普及一下概念,什么是Fitnesse,听一听.NET 版 Cucumber 的创始人 Aslak Hellesøy 谈Fitnesse与 Cucumber 对比:
FIT/Fitnesse和 Cucumber 都执行高级语言编写的验收测试。FIT 仅识别 HTML,Fitnesse 则通过提供 Wiki 语法来简化编写测试的过程。在 FIT/Fitnesse 当中,所有的测试都以表格的形式呈现。
FitNesse 比 Cucumber 的优势在于 Wiki 支持。
原文链接:http://www.infoq.com/cn/news/2009/11/interview-cucumber-for-dotnet
对于 http 请求的测试,有几种方式:
上述 1、2 手工测试,不能用于回归测试;3、4 能用于回归测试,但要开发测试脚本代码,维护非常麻烦。
今日回想起两年前浅尝辄止的fitnesse,想碰运气看看有没有好的测试插件,结果真找到了用于 Restful API 测试的插件:RestFixture
废话少说,先上图:

上面的测试用例的目的是:
<body>
并且 tag 内容是
Don't forget the meeting!
 
note.xml 的内容:
<?xml version="1.0" encoding="ISO-8859-1"?> 
<!--  Copyright w3school.com.cn --> 
<note> 
    <to>George</to> 
    <from>John</from> 
    <heading>Reminder</heading> 
    <body>Don't forget the meeting!</body> 
</note>  
 
fitnesse 测试用例 wiki 文本:
!define TEST_SYSTEM {slim}
!path D:\fitnesse\fitnesse-20111026\smartrics-RestFixture-3.0\lib\*
| Table:smartrics.rest.fitnesse.fixture.RestFixture | http://www.w3school.com.cn |
| GET | /example/xmle/note.xml | 200 | Content-Type: text/xml | //body[text()="Don't forget the meeting!"]|
 
上面的测试用例的目的是:
调用百度地图 Web API,根据 IP 获取地图地址信息,检查服务响应内容中的 status 字段必须是 0,下面判断语句是 javascript 表达式:
jsonbody.status==0
 
GET 访问的 url:
url 返回的内容:
{"address":"CN|\u5409\u6797|\u957f\u6625|None|CERNET|1|None","content":{"address":"\u5409\u6797\u7701\u957f\u6625\u5e02","address_detail":{"city":"\u957f\u6625\u5e02","city_code":53,"district":"","province":"\u5409\u6797\u7701","street":"","street_number":""},"point":{"x":"125.31364243","y":"43.89833761"}},"status":0}
 
被调用 Web API 文档:
fitnesse 测试用例 wiki 文本:
!define TEST_SYSTEM {slim}
!path D:\fitnesse\fitnesse-20111026\smartrics-RestFixture-3.0\lib\*
| Table:smartrics.rest.fitnesse.fixture.RestFixture | http://api.map.baidu.com |
| GET | /location/ip?ip=202.198.16.3&coor=bd09ll&ak=60IFKTCwlIsSpDcGfkx36L8u | 200 | Content-Type: application/json | jsonbody.status==0 |
 我们再来看上面的 GET 响应文本:
{"address":"CN|\u5409\u6797|\u957f\u6625|None|CERNET|1|None","content":{"address":"\u5409\u6797\u7701\u957f\u6625\u5e02","address_detail":{"city":"\u957f\u6625\u5e02","city_code":53,"district":"","province":"\u5409\u6797\u7701","street":"","street_number":""},"point":{"x":"125.31364243","y":"43.89833761"}},"status":0}
 
\u5409\u6797|\u957f\u6625是什么?
按照百度 API 文档,应该返回这样的:
 {  
    address: "CN|吉林|长春|None|CERNET|1|None",  
    content: {  
    address: "吉林省长春市",  
    address_detail: {  
    city: "长春市",  
    city_code: 53,  
    district: "",  
    province: "吉林省",  
    street: "",  
    street_number: ""  
    },  
    point: {       
    x: "125.31364243",      
    y: "43.89833761"  
    }  
    },  
    status: 0  
}
 
原来\u5409\u6797|\u957f\u6625是 吉林 | 长春 encodeURI 的结果
可以在 Chrome 浏览器 > 菜单 > 工具 > JavaScript 控制台(Firefox/IE/Opera 都有对应的控制台)中使用 String.replace 函数来把\u5409 变成"吉":

下一步在 Fitnesse 中要见证奇迹了,RestFixture 居然支持 Javascript 语法和全局函数,也可以调用 String.replace 函数:
!define TEST_SYSTEM {slim}
!path D:\fitnesse\fitnesse-20111026\smartrics-RestFixture-3.0\lib\*
| Table:smartrics.rest.fitnesse.fixture.RestFixture | http://api.map.baidu.com |
| GET | /location/ip?ip=202.198.16.3&coor=bd09ll&ak=60IFKTCwlIsSpDcGfkx36L8u | 200 | Content-Type: application/json | jsonbody.status==0 |
| let | bodyDecoded | js | response.body.replace(/\\u[\dabcdef]{4}/g, function(word){return String.fromCharCode(parseInt(word.substr(2),16))}) | |
 

RestFixture 变量 bodyDecoded 的内容是中文化后的 json:
{"address":"CN|吉林|长春|None|CERNET|1|None","content":{"address":"吉林省长春市","address_detail":{"city":"长春市","city_code":53,"district":"","province":"吉林省","street":"","street_number":""},"point":{"x":"125.31364243","y":"43.89833761"}},"status":0}
 
下面,还可以把 bodyDecoded 作为表达式文本计算,生成一个 js 对象:
| let | printJsonMembers | js | !-
var jsonObject = eval('a=%bodyDecoded%');
var str1="";
function printObject(obj, indent){
  for(var i in obj){
    if(typeof obj[i] == "object"){
      str1+= indent + i+":"+"\n";
      printObject(obj[i], indent+"\t\t");
    } else
      str1+= indent + i +":"+obj[i] + "\n";
  }
}
printObject(jsonObject, "");
str1-! | |
 
这里要演示的第二个 let 行使用了第一个 let 行定义的变量:%bodyDecoded%,显然借鉴了 Windows Bat 文件的语法。
测试结果:
address:CN|吉林|长春|None|CERNET|1|None
content:
        address:吉林省长春市
        address_detail:
                city:长春市
                city_code:53
                district:
                province:吉林省
                street:
                street_number:
        point:
                x:125.31364243
                y:43.89833761
status:0
 https://cloud.github.com/downloads/smartrics/RestFixture/RestFixture-3.0.pdf

fitnesse 运行环境文件目录:


注意:其中的 slf4j-simple-1.6.6.jar 需要额外下载
启动 fitnesse 命令:

D:\fitnesse\fitnesse-20111026>java -jar fitnesse.jar -p 11026
 
-p 11026 是让 fitnesse 网站使用 11026 端口,因为我要在后面的例子中使用多个 fitnesse 版本,所以用版本号作为端口。
RestFixture 在最新版 Fitnesse 20140630 的运行结果:

其中的 GET 行,只有第 2、3 列正确显示,4、5 列显示为 HTML 文本,看上去十分杂乱,
旧版的 Fitnesse 是这样显示的:

后续的博客中,我会介绍如何让 RestFixture 3.0 在 Fitnesse 20140630 中也能打印出漂亮的测试结果。