FavoriteLoading
0

接口测试中数值 diff 和结构 diff 方法

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

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<span class="nb">require</span> <span class="s1">'capybara/json'</span>
<span class="kp">include</span> <span class="no">Capybara</span><span class="o">::</span><span class="no">Json</span>

<span class="no">Capybara</span><span class="p">.</span><span class="nf">current_driver</span> <span class="o">=</span> <span class="ss">:httpclient_json</span>
<span class="no">Capybara</span><span class="p">.</span><span class="nf">app_host</span> <span class="o">=</span> <span class="s1">'http://example.com'</span>
<span class="n">post</span> <span class="s1">'/'</span><span class="p">,</span> <span class="p">{</span> <span class="s2">"this is"</span> <span class="o">=&gt;</span> <span class="s2">"json"</span> <span class="p">}</span> <span class="c1"># POST 'http://example.com/'</span>
<span class="n">json</span>     <span class="c1">#=&gt; parsed json response</span>
<span class="n">raw_json</span> <span class="c1">#=&gt; raw response body</span>

<span class="n">get</span>  <span class="s1">'/errors/400'</span>
<span class="n">status_code</span> <span class="c1">#=&gt; 400</span>
<span class="n">get!</span> <span class="s1">'/errors'</span> <span class="c1">#=&gt; raise Capybara::Json::Error</span>

<span class="n">get</span>  <span class="s1">'/errors'</span><span class="p">,</span> <span class="p">{},</span> <span class="p">{</span> <span class="s1">'header'</span> <span class="o">=&gt;</span> <span class="s1">''</span> <span class="p">}</span> <span class="c1"># set request headers</span>
<span class="n">response_headers</span> <span class="c1">#=&gt; get response headers</span>

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

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
<span class="c1">#coding: utf-8</span>
<span class="nb">require</span> <span class="s1">'rubygems'</span>
<span class="nb">require</span> <span class="s1">'json'</span>
<span class="nb">require</span> <span class="s1">'pp'</span>
<span class="nb">require</span> <span class="s1">'minitest'</span>
<span class="nb">require</span> <span class="s1">'minitest/autorun'</span>
<span class="nb">require</span> <span class="s1">'capybara/json'</span>
<span class="kp">include</span> <span class="no">Capybara</span><span class="o">::</span><span class="no">Json</span>

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

<span class="no">DynamicTest</span> <span class="o">=</span> <span class="no">Struct</span><span class="p">.</span><span class="nf">new</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:expected</span><span class="p">,</span> <span class="ss">:actual</span>
<span class="no">DYNAMIC_TESTS</span> <span class="o">=</span> <span class="p">[]</span>

<span class="no">Capybara</span><span class="p">.</span><span class="nf">current_driver</span> <span class="o">=</span> <span class="ss">:httpclient_json</span>
<span class="c1">#Capybara.app_host = 'http://10.128.6.44:18080'</span>

<span class="n">host</span><span class="o">=</span><span class="s2">"oneapm.com"</span>
<span class="n">index</span><span class="o">=</span><span class="mi">0</span>
<span class="no">Dir</span><span class="p">[</span><span class="s2">"data/*.har"</span><span class="p">].</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">data_file</span><span class="o">|</span>  
    <span class="n">content</span><span class="o">=</span><span class="no">IO</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="n">data_file</span><span class="p">,</span> <span class="ss">:encoding</span><span class="o">=&gt;</span><span class="s2">"UTF-8"</span><span class="p">)</span>
    <span class="n">obj</span> <span class="o">=</span> <span class="no">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">content</span><span class="p">[</span><span class="mi">1</span><span class="p">.</span><span class="nf">.</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>
    <span class="mi">1</span><span class="p">.</span><span class="nf">times</span> <span class="k">do</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span>
        <span class="n">now</span><span class="o">=</span><span class="no">Time</span><span class="p">.</span><span class="nf">now</span><span class="p">.</span><span class="nf">strftime</span><span class="p">(</span><span class="s2">"%s"</span><span class="p">).</span><span class="nf">to_i</span>
        <span class="n">obj</span><span class="p">[</span><span class="s1">'log'</span><span class="p">][</span><span class="s1">'entries'</span><span class="p">].</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">entry</span><span class="o">|</span>
            <span class="n">index</span><span class="o">+=</span><span class="mi">1</span>
            <span class="nb">method</span><span class="o">=</span><span class="s2">""</span>
            <span class="n">entry</span><span class="p">[</span><span class="s1">'request'</span><span class="p">][</span><span class="s1">'url'</span><span class="p">].</span><span class="nf">sub!</span><span class="p">(</span><span class="s2">"10.128.6.44:18080"</span><span class="p">,</span> <span class="n">host</span><span class="p">)</span>

            <span class="k">begin</span>
                <span class="n">now</span><span class="o">=</span><span class="no">Time</span><span class="p">.</span><span class="nf">now</span><span class="p">.</span><span class="nf">strftime</span><span class="p">(</span><span class="s2">"%s"</span><span class="p">).</span><span class="nf">to_i</span><span class="o">-</span><span class="mi">60</span>
                <span class="k">if</span> <span class="nb">method</span><span class="o">==</span><span class="s1">'metric_data'</span>
                    <span class="n">data</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">=</span><span class="p">(</span><span class="n">now</span><span class="o">-</span><span class="mi">60</span><span class="p">)</span>
                    <span class="n">data</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="o">=</span><span class="n">now</span>
                <span class="k">end</span>
                <span class="n">post</span>  <span class="n">entry</span><span class="p">[</span><span class="s1">'request'</span><span class="p">][</span><span class="s1">'url'</span><span class="p">],</span> <span class="n">data</span><span class="p">.</span><span class="nf">to_json</span>
                <span class="n">testcase_name</span><span class="o">=</span><span class="s2">"test_</span><span class="si">#{</span><span class="n">data_file</span><span class="p">.</span><span class="nf">gsub</span><span class="p">(</span><span class="s1">'/'</span><span class="p">,</span> <span class="s1">'_'</span><span class="p">)</span><span class="si">}</span><span class="s2">_</span><span class="si">#{</span><span class="nb">method</span><span class="si">}</span><span class="s2">_</span><span class="si">#{</span><span class="n">index</span><span class="p">.</span><span class="nf">to_s</span><span class="si">}</span><span class="s2">"</span>
                <span class="nb">p</span> <span class="s2">"create testcase </span><span class="si">#{</span><span class="n">testcase_name</span><span class="si">}</span><span class="s2">"</span>

<span class="c1">#动态生成值对比的单测用例</span>
                <span class="no">DYNAMIC_TESTS</span> <span class="o">&lt;&lt;</span> <span class="no">DynamicTest</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">testcase_name</span><span class="o">+</span><span class="s2">"_diff_value"</span><span class="p">,</span> <span class="n">raw_json</span><span class="p">,</span> <span class="n">entry</span><span class="p">[</span><span class="s1">'response'</span><span class="p">][</span><span class="s1">'content'</span><span class="p">][</span><span class="s1">'text'</span><span class="p">])</span>
                <span class="k">begin</span>
                    <span class="n">expect_s</span><span class="o">=</span><span class="n">to_struct</span><span class="p">(</span><span class="no">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">entry</span><span class="p">[</span><span class="s1">'response'</span><span class="p">][</span><span class="s1">'content'</span><span class="p">][</span><span class="s1">'text'</span><span class="p">]))</span>
                <span class="k">rescue</span>
                    <span class="n">expect_s</span><span class="o">=</span><span class="s2">"json parse error"</span>
                <span class="k">end</span>
                <span class="k">begin</span>
                    <span class="n">actual_s</span><span class="o">=</span><span class="n">to_struct</span><span class="p">(</span><span class="no">JSON</span><span class="p">.</span><span class="nf">parse</span><span class="p">(</span><span class="n">raw_json</span><span class="p">))</span>
                <span class="k">rescue</span>
                    <span class="n">actual_s</span><span class="o">=</span><span class="s2">"json parse error"</span>
                <span class="k">end</span>
<span class="c1">#动态生成结构对比的单测用例</span>
                <span class="no">DYNAMIC_TESTS</span> <span class="o">&lt;&lt;</span> <span class="no">DynamicTest</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">testcase_name</span><span class="o">+</span><span class="s2">"_diff_struct"</span><span class="p">,</span> <span class="n">expect_s</span><span class="p">,</span> <span class="n">actual_s</span><span class="p">)</span>
            <span class="k">rescue</span>
                <span class="nb">p</span> <span class="s2">"exception"</span>
                <span class="nb">p</span> <span class="n">entry</span><span class="p">[</span><span class="s1">'request'</span><span class="p">][</span><span class="s1">'url'</span><span class="p">]</span>
            <span class="k">end</span>

        <span class="k">end</span>
        <span class="nb">sleep</span> <span class="mi">1</span>
    <span class="k">end</span>
<span class="k">end</span>

<span class="c1">#动态生成单测方法</span>
<span class="k">class</span> <span class="nc">HARDiff</span> <span class="o">&lt;</span>  <span class="no">MiniTest</span><span class="o">::</span><span class="no">Test</span>
    <span class="k">class</span> <span class="o">&lt;&lt;</span> <span class="nb">self</span>
        <span class="k">def</span> <span class="nf">create_method</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
            <span class="n">define_method</span><span class="p">(</span><span class="n">t</span><span class="p">.</span><span class="nf">name</span><span class="p">)</span> <span class="k">do</span>
                <span class="n">assert_equal</span> <span class="n">t</span><span class="p">.</span><span class="nf">expected</span><span class="p">,</span> <span class="n">t</span><span class="p">.</span><span class="nf">actual</span>
            <span class="k">end</span>
        <span class="k">end</span>
    <span class="k">end</span>
<span class="k">end</span>
<span class="no">DYNAMIC_TESTS</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">t</span><span class="o">|</span>
    <span class="no">HARDiff</span><span class="p">.</span><span class="nf">create_method</span> <span class="n">t</span>
<span class="k">end</span>

代码经过裁剪, 仅供参考

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

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

声明:本文转载自 TesterHome 移动测试社区,作者为 TesterHome 移动测试社区,原文网址:https://testerhome.com/topics/2465

最后编辑于:2016/11/19作者: 聚合

聚合类文章源自互联网, 感谢原作者的无私分享。

关注微信公众号 – 聚合软件测试类精华

关注微信公众号 – 聚合软件测试类精华