在接口测试过程中,当所测接口存在多个版本时,经常遇到需要对新旧接口返回值进行对比测试(diff 测试)。
方法 1. 将新旧接口返回值以文件存储,然后利用文件对比、json 对比的工具逐一比较进行测试。
方法 2. 自动化测试,使用 python、javascript、java 中断言进行对比。
package com.test.apitest;
import com.alibaba.fastjson.JSON;
import org.junit.*;
import java.util.Map;
public class JunitJsonDiffTest {
@Test
public void test_json_diff_sub_nodes(){
String oldJson = "{\"user\":{\"name\":\"test\",\"age\":10},\"hello\":\"world\",\"success\":true}";
String newJson = "{\"success\":true,\"hello\":\"world\",\"user\":{\"name\":\"test\",\"age\":10}}";
Object oldJsonVo = JSON.parseObject(oldJson);
Object newJsonVo = JSON.parseObject(newJson);
// json字段顺序虽然不一样,但是对比结果相同
Assert.assertEquals(oldJsonVo,newJsonVo);
}
@Test
public void test_json_with_date(){
String oldJson = "{\"hello\":\"你好\",\"success\":true,\"birthDay\":\"1995-06-15 10:05:00\"}";
String newJson = "{\"hello\":\"你好\",\"success\":true,\"birthDay\":\"1995-06-15 20:05:00\"}";
Object oldJsonVo = JSON.parseObject(oldJson);
Object newJsonVo = JSON.parseObject(newJson);
// 对比过程中需要考虑是否支持中文
Assert.assertEquals(oldJsonVo,newJsonVo);
}
}
2. 对比效果
3. 使用 mvn 命令生成 html 报告
pom.xml 文件中增加 maven-surefire-report 依赖项:
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-report-plugin</artifactId>
<version>2.22.0</version>
</dependency>
生成 html 报告命令:
mvn clean surefire-report:report
import json
class TestDictCompare(unittest.TestCase):
def test_jsondiff_same(self):
self.maxDiff=None
oldJson = json.loads('{"title":"com.tencent.news","fullTitle":"Androbugs report com.tencent.news","timedOut":false,"duration":0,"state":"passed","speed":"fast","pass":true,"fail":false,"pending":false}')
newJson = json.loads('{"title":"com.tencent.news","fullTitle":"Androbugs report com.tencent.news","timedOut":false,"duration":0,"state":"passed","speed":"fast","pass":true,"fail":false,"pending":false}')
self.assertDictEqual(oldJson,newJson)
def test_jsondiff_different(self):
self.maxDiff=None
oldJson = json.loads('{"title":"com.tencent.news","fullTitle":"Androbugs report com.tencent.news","timedOut":false,"duration":0,"state":"passed","speed":"fast","pass":true,"fail":false,"pending":false}')
newJson = json.loads('{"title":"com.tencent.news2","fullTitle":"Androbugs report com.tencent.news","timedOut":false,"duration":0,"state":"passed","speed":"fast","pass":true,"fail":false,"pending":false}')
self.assertDictEqual(oldJson,newJson)
if __name__ == '__main__':
unittest.main()
2. 运行结果
3. 使用 pip 安装 pytest-html 包生成 html 报告
pip install pytest-html
pytest --html=sondiff.html test\test_jsondiff.py
var assert = require("chai").assert;
describe("json diff", function () {
it("json相同", function () {
var oldJson = JSON.parse('{"title":"com.tencent.news","fullTitle":"Androbugs report com.tencent.news","timedOut":false,"duration":0,"state":"passed","speed":"fast","pass":true,"fail":false,"pending":false}')
var newJson = JSON.parse('{"title":"com.tencent.news","fullTitle":"Androbugs report com.tencent.news","timedOut":false,"duration":0,"state":"passed","speed":"fast","pass":true,"fail":false,"pending":false}')
assert.deepEqual(oldJson, newJson)
})
it("json不同", function () {
var oldJson = JSON.parse('{"title":"com.tencent.news","fullTitle":"Androbugs report com.tencent.news","timedOut":false,"duration":0,"state":"passed","speed":"fast","pass":true,"fail":false,"pending":false}')
var newJson = JSON.parse('{"title":"com.tencent.news2","fullTitle":"Androbugs report com.tencent.news","timedOut":false,"duration":0,"state":"passed","speed":"fast","pass":true,"fail":false,"pending":false}')
assert.deepEqual(oldJson, newJson)
})
})
2. 运行结果
3. 使用 mochawesome 生成 html 报告
npm install mochawesome --save-dev
mocha test\jsondiff.test.js --reporter mochawesome
原理:在 JAVA、python 中将需要对比的接口 URL 进行参数化,使用一个用例来进行 diff 测试。在 JavaScript 中将接口 url 及其参数放到一个参数数组中,使用数组 forEach 方法来进行对比测试。
package com.test.apitest;
import com.alibaba.fastjson.JSON;
import org.junit.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.util.Map;
import com.github.kevinsawicki.http.HttpRequest;
public class BatchApiTest {
@DataProvider
public Object[][] data() {
Object[][] diffApis = new Object[][]{
{
"https://easy-mock.com/mock/5b6b06f9a40bfb27425bbb6a/jsondiff/v1/demo",
"https://easy-mock.com/mock/5b6b06f9a40bfb27425bbb6a/jsondiff/v2/demo"
},
{
"https://easy-mock.com/mock/5b6b06f9a40bfb27425bbb6a/jsondiff/v1/hello",
"https://easy-mock.com/mock/5b6b06f9a40bfb27425bbb6a/jsondiff/v2/hello"
},
{
"https://easy-mock.com/mock/5b6b06f9a40bfb27425bbb6a/jsondiff/v1/user",
"https://easy-mock.com/mock/5b6b06f9a40bfb27425bbb6a/jsondiff/v2/user"
},
{
"https://easy-mock.com/mock/5b6b06f9a40bfb27425bbb6a/jsondiff/v1/1/list",
"https://easy-mock.com/mock/5b6b06f9a40bfb27425bbb6a/jsondiff/v2/1/list"
},
{
"https://easy-mock.com/mock/5b6b06f9a40bfb27425bbb6a/jsondiff/v1/2/list",
"https://easy-mock.com/mock/5b6b06f9a40bfb27425bbb6a/jsondiff/v2/2/list"
},
{
"https://easy-mock.com/mock/5b6b06f9a40bfb27425bbb6a/jsondiff/v1/3/list",
"https://easy-mock.com/mock/5b6b06f9a40bfb27425bbb6a/jsondiff/v2/3/list"
},
};
return diffApis;
}
@Test(dataProvider = "data")
public void test_apiDiff(String oldApiUrl, String newApiUrl) {
String oldJsonString = HttpRequest.get(oldApiUrl)
.accept("application/json")
.body();
String newJsonString = HttpRequest.get(newApiUrl)
.accept("application/json")
.body();
Map<String,Object> oldJsonVo = JSON.parseObject(oldJsonString);
Map<String,Object> newJsonVo = JSON.parseObject(newJsonString);
Assert.assertEquals(oldJsonVo,newJsonVo);
}
}
# -*- coding:utf-8 -*-
import unittest
import requests
from parameterized import parameterized, param
diffApis = [
# diff apis
{
"oldapi":"https://easy-mock.com/mock/5b6b06f9a40bfb27425bbb6a/jsondiff/v1/demo",
"newapi":"https://easy-mock.com/mock/5b6b06f9a40bfb27425bbb6a/jsondiff/v2/demo"
},
{
"oldapi":"https://easy-mock.com/mock/5b6b06f9a40bfb27425bbb6a/jsondiff/v1/hello",
"newapi":"https://easy-mock.com/mock/5b6b06f9a40bfb27425bbb6a/jsondiff/v2/hello"
},
{
"oldapi":"https://easy-mock.com/mock/5b6b06f9a40bfb27425bbb6a/jsondiff/v1/user",
"newapi":"https://easy-mock.com/mock/5b6b06f9a40bfb27425bbb6a/jsondiff/v2/user"
},
{
"oldapi":"https://easy-mock.com/mock/5b6b06f9a40bfb27425bbb6a/jsondiff/v1/1/list",
"newapi":"https://easy-mock.com/mock/5b6b06f9a40bfb27425bbb6a/jsondiff/v2/1/list"
},
{
"oldapi":"https://easy-mock.com/mock/5b6b06f9a40bfb27425bbb6a/jsondiff/v1/2/list",
"newapi":"https://easy-mock.com/mock/5b6b06f9a40bfb27425bbb6a/jsondiff/v2/2/list"
},
{
"oldapi":"https://easy-mock.com/mock/5b6b06f9a40bfb27425bbb6a/jsondiff/v1/3/list",
"newapi":"https://easy-mock.com/mock/5b6b06f9a40bfb27425bbb6a/jsondiff/v2/3/list"
},
]
class TestParameterized(unittest.TestCase):
@parameterized.expand(
[param("diff test",kv) for kv in diffApis])
def test_batchdiff(self, _, kv):
self.maxDiff = None
oldRes = requests.get(kv["oldapi"])
newRes = requests.get(kv["newapi"])
self.assertDictEqual(oldRes.json(), newRes.json())
if __name__ == '__main__':
unittest.main(verbosity=2)
var assert = require('chai').assert;
var request = require('request');
describe("Node.js版本批量接口diff", function () {
this.timeout(0);
[
// diff apis
{
oldapi: "https://mtj.baidu.com/data/mobile/trend?dimension=brand&platformId=0&rankId=16&stattime=1527782400",
newapi: "https://mtj.baidu.com/data/mobile/trend?dimension=brand&platformId=0&rankId=16&stattime=1527782400"
},
{
oldapi: "https://mtj.baidu.com/data/mobile/trend?dimension=brand&platformId=0&rankId=16&stattime=1527782400",
newapi: "https://mtj.baidu.com/data/mobile/trend?dimension=brand&platformId=0&rankId=4&stattime=1527782400"
},
{
oldapi: "https://mtj.baidu.com/data/mobile/trend?dimension=brand&platformId=0&rankId=16&stattime=1527782400",
newapi: "https://mtj.baidu.com/data/mobile/trend?dimension=brand&platformId=0&rankId=23&stattime=1527782400"
},
{
oldapi: "https://mtj.baidu.com/data/mobile/trend?dimension=brand&platformId=0&rankId=16&stattime=1527782400",
newapi: "https://mtj.baidu.com/data/mobile/trend?dimension=brand&platformId=0&rankId=12&stattime=1527782400"
},
{
oldapi: "https://mtj.baidu.com/data/mobile/trend?dimension=brand&platformId=0&rankId=16&stattime=1527782400",
newapi: "https://mtj.baidu.com/data/mobile/trend?dimension=brand&platformId=0&rankId=2&stattime=1527782400"
},
{
oldapi: "https://mtj.baidu.com/data/mobile/trend?dimension=brand&platformId=0&rankId=16&stattime=1527782400",
newapi: "https://mtj.baidu.com/data/mobile/trend?dimension=brand&platformId=0&rankId=15&stattime=1527782400"
},
{
oldapi: "http://wis.qq.com/weather/common?callback=jQuery111105922904533110209_1533657946519&weather_type=observe%7Cforecast_24h%7Cair&source=pc&province=%E5%8C%97%E4%BA%AC%E5%B8%82&city=%E5%8C%97%E4%BA%AC%E5%B8%82&county=&_=1533657946520",
newapi: "http://wis.qq.com/weather/common?callback=jQuery111105922904533110209_1533657946519&weather_type=observe%7Cforecast_24h%7Cair&source=pc&province=%E5%8C%97%E4%BA%AC%E5%B8%82&city=%E5%8C%97%E4%BA%AC%E5%B8%82&county=&_=1533657946520"
},
{
oldapi: "http://wis.qq.com/weather/common?source=pc&weather_type=observe|alarm&province=%E5%8C%97%E4%BA%AC%E5%B8%82&city=%E5%8C%97%E4%BA%AC%E5%B8%82&county=&callback=jQuery112008061541882788377_1533657948645&_=1533657948647",
newapi: "http://wis.qq.com/weather/common?source=pc&weather_type=observe|alarm&province=%E5%8C%97%E4%BA%AC%E5%B8%82&city=%E5%8C%97%E4%BA%AC%E5%B8%82&county=&callback=jQuery112008061541882788377_1533657948645&_=1533657948647"
}
].forEach(function (kv) {
it(kv.newapi + " vs " + kv.oldapi, function (done) {
request.get(kv.newapi, {
json: true
},
function (err, res, body) {
request.get(kv.oldapi, {
json: true
},
function (oldapiError, oldapiRes, oldapiBody) {
assert.deepEqual(body, oldapiBody);
done();
}
);
}
);
});
});
});