今年的 QCon 上, 七牛的人介绍了下他们的接口测试体系. 我发现碰巧和我们公司做的一样.
他说他的测试体系是用 go 实现的, 并且说思想是来自于...

不过类似的测试框架 ruby 早就有了. 今天就给大家分享下.
开源项目地址 https://github.com/okitan/capybara-json
样例代码如下

require 'capybara/json'
include Capybara::Json

Capybara.current_driver = :httpclient_json
Capybara.app_host = 'http://example.com'
post '/', { "this is" => "json" } # POST 'http://example.com/'
json     #=> parsed json response
raw_json #=> raw response body

get  '/errors/400'
status_code #=> 400
get! '/errors' #=> raise Capybara::Json::Error

get  '/errors', {}, { 'header' => '' } # set request headers
response_headers #=> get response headers

是不是非常的简单. 我们公司的接口测试就是基于这个小框架做的.
capybara 是什么那. 是一个新的测试框架.他也是 Rails 的作者极力推崇的一个框架. 他的功能太强大了, 以至于我不能在此介绍它.
大家可以自己去 github 上看看.

我在公司开发了一个接口测试工具. 这个工具的实现思路如下.
利用 fiddler 或者其他的反向代理工具, 截获各个模块之间的请求. 然后保存下来.比如 fiddler 就有个导出为 HAR 格式的功能.
然后解析这个 HAR, 发送之前录制好的请求, 然后拿系统实际返回的结果与之前 HAR 中保存的结果进行比对.
为了统计结果, 所以使用了 xunit 框架. 因为这是数据驱动的方式, 不是硬编码. 所以需要用到一个单测的 hack 技巧. 动态生成测试用例.

#coding: utf-8
require 'rubygems'
require 'json'
require 'pp'
require 'minitest'
require 'minitest/autorun'
require 'capybara/json'
include Capybara::Json

#把返回的json解析为结构, 去掉具体的数字. 比如"xxx"会被解析为"String"
def to_struct(h)
    if h.class==Hash
        new=h.clone
        h.map do |k,v|
            new[k]=to_struct(v)
        end
    end
    if h.class==Array
        new=[]
        h.each do |x|
            new << to_struct(x)
        end
    end
    if h.class!=Array && h.class!=Hash
        new=h.class.to_s
    end
    new 
end

DynamicTest = Struct.new :name, :expected, :actual
DYNAMIC_TESTS = []

Capybara.current_driver = :httpclient_json
#Capybara.app_host = 'http://10.128.6.44:18080'

host="oneapm.com"
index=0
Dir["data/*.har"].each do |data_file|  
    content=IO.read(data_file, :encoding=>"UTF-8")
    obj = JSON.parse(content[1..-1])
    1.times do |i|
        now=Time.now.strftime("%s").to_i
        obj['log']['entries'].each do |entry|
            index+=1
            method=""
            entry['request']['url'].sub!("10.128.6.44:18080", host)

            begin
                now=Time.now.strftime("%s").to_i-60
                if method=='metric_data' 
                    data[1]=(now-60)
                    data[2]=now
                end
                post  entry['request']['url'], data.to_json
                testcase_name="test_#{data_file.gsub('/', '_')}_#{method}_#{index.to_s}"
                p "create testcase #{testcase_name}"

#动态生成值对比的单测用例
                DYNAMIC_TESTS << DynamicTest.new(testcase_name+"_diff_value", raw_json, entry['response']['content']['text'])
                begin
                    expect_s=to_struct(JSON.parse(entry['response']['content']['text']))
                rescue
                    expect_s="json parse error"
                end
                begin
                    actual_s=to_struct(JSON.parse(raw_json))
                rescue
                    actual_s="json parse error"
                end
#动态生成结构对比的单测用例
                DYNAMIC_TESTS << DynamicTest.new(testcase_name+"_diff_struct", expect_s, actual_s)
            rescue
                p "exception"
                p entry['request']['url']
            end

        end
        sleep 1
    end
end

#动态生成单测方法
class HARDiff <  MiniTest::Test
    class << self
        def create_method(t)
            define_method(t.name) do
                assert_equal t.expected, t.actual
            end
        end
    end
end
DYNAMIC_TESTS.each do |t|
    HARDiff.create_method t
end

代码经过裁剪, 仅供参考

代码非常的少, 主要是特点如下

  1. 可以利用 fiddler 或者 chrome 导出某个系统的接口请求列表. 导出为 HAR 格式
  2. 读取 HAR 格式, 进行接口 diff 测试, 这种测试属于专项测试. 他只是为了保证新老系统的接口一致.
  3. 接口 DIff 方法分两个模式.一个是相同的请求返回的内容完全相同. 一个是返回的内容格式相同即可. 有些值可以不同. 我把返回的 json 结果进行了变幻.支持结构对比.
  4. 重新发送请求的时候,有些字段如果需要修改比如时间戳等, 需要自己自定义.
  5. 利用 ci-reporter 插件把单测的结果导出为 xml 并放到 jenkins 上解析.

使用场景是
提前录制好系统的交互. 然后新版本环境上线. 使用录制好的数据回放请求并验证接口的变更情况. 这种方式简单粗暴.但是可以保证接口的稳定.


↙↙↙阅读原文可查看相关链接,并与作者交流