├── .gitignore ├── .travis.yml ├── report-demo.png ├── src ├── main │ ├── resources │ │ ├── banner.png │ │ ├── banner.txt │ │ ├── application.properties │ │ ├── META-INF │ │ │ └── additional-spring-configuration-metadata.json │ │ └── templates │ │ │ └── html-report.ftl │ └── java │ │ └── com │ │ └── qianmi │ │ └── tda │ │ ├── util │ │ ├── DubboExecutorUtil.java │ │ ├── SpringContextHolder.java │ │ ├── Tools.java │ │ └── EvalUtil.java │ │ ├── bean │ │ ├── TestCase.java │ │ ├── TestSuit.java │ │ ├── DubboRequest.java │ │ ├── TestResult.java │ │ └── AggTestResult.java │ │ ├── report │ │ └── HtmlReportGenerator.java │ │ ├── exec │ │ ├── DubboExecutor.java │ │ ├── TestCaseLoader.java │ │ └── DubboTestRunner.java │ │ ├── config │ │ └── HttpClientConfiguration.java │ │ └── InterfaceTestApplication.java └── test │ ├── resources │ ├── application.properties │ └── nashorn.js │ └── java │ └── com │ └── qianmi │ └── tda │ ├── util │ ├── ToolsTest.java │ └── EvalUtilTest.java │ ├── report │ └── HtmlReportGeneratorTest.java │ ├── TestCaseLoaderTest.java │ ├── NashornTest.java │ └── TestCaseGenerator.java ├── pom.xml ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | .idea 3 | testcase -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | sudo: false 4 | 5 | jdk: 6 | - oraclejdk8 -------------------------------------------------------------------------------- /report-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QianmiOpen/interface-test/HEAD/report-demo.png -------------------------------------------------------------------------------- /src/main/resources/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QianmiOpen/interface-test/HEAD/src/main/resources/banner.png -------------------------------------------------------------------------------- /src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | ${Ansi.GREEN} :: Sample application build with Spring Boot${spring-boot.formatted-version} ::${Ansi.DEFAULT} 2 | -------------------------------------------------------------------------------- /src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | name: Phil 2 | 3 | logging.level.root=info 4 | logging.level.com.qianmi=info 5 | 6 | http.client.connection-manager-default-max-per-route=1000 7 | http.client.request-connect-timeout=2000 8 | http.client.connection-manager-max-total=1000 9 | http.client.request-read-timeout=10000 -------------------------------------------------------------------------------- /src/main/java/com/qianmi/tda/util/DubboExecutorUtil.java: -------------------------------------------------------------------------------- 1 | package com.qianmi.tda.util; 2 | 3 | import com.qianmi.tda.exec.DubboExecutor; 4 | 5 | import java.io.IOException; 6 | 7 | /** 8 | * DubboExecutorUtil 9 | * Created by aqlu on 2016/11/10. 10 | */ 11 | @SuppressWarnings("unused") 12 | public class DubboExecutorUtil { 13 | 14 | public static String exec(String request) throws IOException { 15 | DubboExecutor dubboExecutor = SpringContextHolder.getBean(DubboExecutor.class); 16 | return dubboExecutor.exec(request); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/qianmi/tda/bean/TestCase.java: -------------------------------------------------------------------------------- 1 | package com.qianmi.tda.bean; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * TestCase 10 | * Created by aqlu on 2016/10/28. 11 | */ 12 | @Data 13 | public class TestCase { 14 | private String name; 15 | 16 | private List params = new ArrayList<>(); 17 | 18 | private List expects = new ArrayList<>(); 19 | 20 | @SuppressWarnings("WeakerAccess") 21 | @Data 22 | public static class Expect { 23 | private String path; 24 | 25 | private Object value; 26 | 27 | /** 28 | * 运算符;equals、gt、gte、lt、lte、contains、match 29 | */ 30 | private String operator; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | logging.level.root=info 2 | logging.level.com.qianmi=debug 3 | logging.level.test-loader=debug 4 | logging.level.com.qianmi.tda.report=info 5 | 6 | ## \u6D4B\u8BD5\u7528\u4F8B\u6839\u76EE\u5F55 7 | test-suit-home=${user.dir}/testcase 8 | ## \u6D4B\u8BD5\u7528\u4F8B\u6587\u4EF6\u6269\u5C55\u540D 9 | #test-case-file-extensions=.ts.json,.ts.js 10 | #test-suit-variable-name-in-js=testSuit 11 | ## \u6D4B\u8BD5\u62A5\u544A\u6839\u76EE\u5F55 12 | test-reports-home=${user.dir}/reports 13 | ## \u9ED8\u8BA4\u6D4B\u8BD5\u670D\u52A1\u5668\u5730\u5740 14 | default-test-server-url=http://172.19.65.96:8080/executeTest.do 15 | 16 | http.client.connection-manager-default-max-per-route=1000 17 | http.client.request-connect-timeout=2000 18 | http.client.connection-manager-max-total=1000 19 | http.client.request-read-timeout=10000 20 | -------------------------------------------------------------------------------- /src/main/java/com/qianmi/tda/bean/TestSuit.java: -------------------------------------------------------------------------------- 1 | package com.qianmi.tda.bean; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * TestSuit 10 | * Created by aqlu on 2016/10/28. 11 | */ 12 | @Data 13 | public class TestSuit implements Comparable { 14 | 15 | private String name; 16 | 17 | private String intfName; 18 | 19 | private String testServerURL; 20 | 21 | private String dubboServiceURL; 22 | 23 | private Long execOrder = 1L; 24 | 25 | private List testCases = new ArrayList<>(); 26 | 27 | @Override 28 | public int compareTo(TestSuit testSuit) { 29 | if (this.execOrder.compareTo(testSuit.getExecOrder()) == 0 && this.intfName != null && testSuit.getIntfName() != null) { 30 | return this.intfName.compareTo(testSuit.getIntfName()); 31 | } else { 32 | return this.execOrder.compareTo(testSuit.getExecOrder()); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/qianmi/tda/bean/DubboRequest.java: -------------------------------------------------------------------------------- 1 | package com.qianmi.tda.bean; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * DubboRequest 13 | * Created by aqlu on 2016/11/10. 14 | */ 15 | @SuppressWarnings("unused") 16 | @Data 17 | @Getter 18 | @Setter 19 | public class DubboRequest { 20 | 21 | private String intfName; 22 | 23 | private String testServerURL; 24 | 25 | private String dubboServiceURL; 26 | 27 | private List params = new ArrayList<>(); 28 | 29 | public DubboRequest(){ 30 | 31 | } 32 | 33 | @Builder 34 | public DubboRequest(String intfName, String testServerURL, String dubboServiceURL, List params){ 35 | this.intfName = intfName; 36 | this.testServerURL = testServerURL; 37 | this.dubboServiceURL = dubboServiceURL; 38 | this.params = params; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/qianmi/tda/util/SpringContextHolder.java: -------------------------------------------------------------------------------- 1 | package com.qianmi.tda.util; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.context.ApplicationContextAware; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * 获取spring上下文工具类 10 | * Created by aqlu on 15/12/2. 11 | */ 12 | @SuppressWarnings({"unused", "WeakerAccess"}) 13 | @Component 14 | public class SpringContextHolder implements ApplicationContextAware { 15 | private static ApplicationContext applicationContext; 16 | 17 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 18 | SpringContextHolder.applicationContext = applicationContext; 19 | } 20 | 21 | public static ApplicationContext getApplicationContext() { 22 | return applicationContext; 23 | } 24 | 25 | /** 26 | * 根据Bean名称获取实例 27 | * 28 | * @return bean实例 29 | */ 30 | @SuppressWarnings("unchecked") 31 | public static T getBean(String name) throws BeansException { 32 | return (T)applicationContext.getBean(name); 33 | } 34 | 35 | /** 36 | * 根据类型获取实例 37 | * 38 | * @param type 类型 39 | * @return bean实例 40 | */ 41 | public static T getBean(Class type) throws BeansException { 42 | return applicationContext.getBean(type); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/com/qianmi/tda/util/ToolsTest.java: -------------------------------------------------------------------------------- 1 | package com.qianmi.tda.util; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * ToolsTest 9 | * Created by aqlu on 2016/11/3. 10 | */ 11 | public class ToolsTest { 12 | @Test 13 | public void formatJson() throws Exception { 14 | String json = "{\"exec\nOrder\":1,\"testCases\":[{\"intfName\":\"case1\",\"params\":[{\"brandName\":\"茶类\",\"cateId\":\"1008\",\"chainMasterId\":\"A1246490\",\"goodsModifyRequest\":[{\"optUserCode\":null,\"optUserName\":null,\"goodsId\":\"g3071818\",\"price\":20,\"stock\":1000,\"spec\":null,\"barCode\":null,\"cost\":0}],\"imagesRequest\":[],\"optUserCode\":null,\"optUserName\":null,\"pointUserId\":\"A1452239\",\"productDesc\":\"

      新疆昆仑雪菊是野生草本植物,性味苦、辛、韦寒、归肺、肝经。是新疆惟一与雪莲齐名的稀有高寒植物,《本草汇言》称其可“破血疏肝,解疔散毒。是一种人们非常喜欢的珍贵食材,虽然很多人都还无缘尝得。那么,雪菊泡水喝的功效有什么?主要对高血压,高血脂,糖尿病有着特殊的药食疗效。同时还可以平肝明目,散风清热,抗菌消炎。用于防止上呼吸道感染,失眠多梦,改善睡眠,热性肠炎及前列腺炎,消除口臭,改善眼睛的各种不适,对治疗眼睛疲劳,视力模糊有很好的疗效。

\",\"productId\":\"1455153\",\"productName\":\"新疆昆仑雪菊盒装50克\",\"productPlace\":null,\"supplier\":null,\"unit\":\"盒\"}],\"expects\":[{\"path\":\"$\",\"value\":null,\"operator\":\"=\"}]}]}"; 15 | System.out.println(Tools.formatJson(json)); 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /src/main/java/com/qianmi/tda/bean/TestResult.java: -------------------------------------------------------------------------------- 1 | package com.qianmi.tda.bean; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * TestCase 12 | * Created by aqlu on 2016/10/27. 13 | */ 14 | @Data 15 | public class TestResult { 16 | public static String PASSED = "passed"; 17 | public static String FAILED = "failed"; 18 | public static String ERROR = "error"; 19 | 20 | private String name; 21 | 22 | private String status; //Success or Failed or Error 23 | 24 | private long costMills; 25 | 26 | private String date; 27 | 28 | private String exception; 29 | 30 | private String originResult; 31 | 32 | @Builder 33 | public TestResult(String name, String status, long costMills, String date, String exception){ 34 | this.name = name; 35 | this.status = status; 36 | this.costMills = costMills; 37 | this.date = date; 38 | this.exception = exception; 39 | } 40 | 41 | @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") 42 | private List failMsgList = new ArrayList<>(); 43 | 44 | public TestResult addFailMsgs(ListfailMsgs){ 45 | this.failMsgList.addAll(failMsgs); 46 | return this; 47 | } 48 | 49 | @Data 50 | @AllArgsConstructor 51 | public static class FailMsg{ 52 | private Object actual; 53 | 54 | private Object expect; 55 | 56 | private String operator; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/test/resources/nashorn.js: -------------------------------------------------------------------------------- 1 | var testSuit= function () { 2 | return { 3 | "dubboServiceURL": "dubbo://172.19.65.199:20880", 4 | "execOrder": 1, 5 | "intfName": "com.qianmi.pc.api.app.AppProductProvider:1.0.0@getGoodsStockList", 6 | "testCases": [ 7 | { 8 | "name": "case1", 9 | "params": [ 10 | { 11 | "chainMasterId": "A1463955", 12 | "goodsIdList": null, 13 | "optUserCode": "A1463955", 14 | "optUserName": "lstlyz138", 15 | "pageNum": 0, 16 | "pageSize": 10, 17 | "productName": "", 18 | "skuBn": "", 19 | "specString": null, 20 | "stockWarnEndTime": null, 21 | "stockWarnEnum": "WARN", 22 | "stockWarnStartTime": null 23 | } 24 | ], 25 | "expects": [ 26 | { 27 | "operator": "=", 28 | "path": "$", 29 | "value": { 30 | "dataList": [ 31 | 32 | ], 33 | "totalCount": 0, 34 | "pageNum": 0, 35 | "pageSize": 10 36 | } 37 | } 38 | ] 39 | } 40 | ], 41 | "testServerURL": null 42 | }; 43 | }(); -------------------------------------------------------------------------------- /src/main/java/com/qianmi/tda/bean/AggTestResult.java: -------------------------------------------------------------------------------- 1 | package com.qianmi.tda.bean; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | import lombok.Getter; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | /** 12 | * AggTestCase 13 | * Created by aqlu on 2016/10/27. 14 | */ 15 | @Data 16 | public class AggTestResult { 17 | 18 | private String name; 19 | 20 | private String date; 21 | 22 | @Builder 23 | public AggTestResult(String name, String date) { 24 | this.name = name; 25 | this.date = date; 26 | } 27 | 28 | @Getter 29 | private List testResultList = new ArrayList<>(); 30 | 31 | public AggTestResult addTestCases(List testResults) { 32 | this.testResultList.addAll(testResults); 33 | return this; 34 | } 35 | 36 | public long getFailures() { 37 | return this.testResultList.stream().filter(testResultResult -> testResultResult.getStatus().equals(TestResult.FAILED)).count(); 38 | } 39 | 40 | public long getPassed() { 41 | return this.testResultList.stream().filter(testResultResult -> testResultResult.getStatus().equals(TestResult.PASSED)).count(); 42 | } 43 | 44 | public long getErrors() { 45 | return this.testResultList.stream().filter(testResultResult -> testResultResult.getStatus().equals(TestResult.ERROR)).count(); 46 | } 47 | 48 | public long getTotal() { 49 | return this.testResultList.size(); 50 | } 51 | 52 | public long getTotalTime() { 53 | return this.testResultList.stream().mapToLong(TestResult::getCostMills).sum(); 54 | } 55 | 56 | public String getStatus() { 57 | if (getErrors() > 0) { 58 | return TestResult.ERROR; 59 | } else if (getFailures() > 0) { 60 | return TestResult.FAILED; 61 | } else { 62 | return TestResult.PASSED; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/test/java/com/qianmi/tda/report/HtmlReportGeneratorTest.java: -------------------------------------------------------------------------------- 1 | package com.qianmi.tda.report; 2 | 3 | import com.qianmi.tda.bean.AggTestResult; 4 | import com.qianmi.tda.bean.TestResult; 5 | import com.qianmi.tda.util.Tools; 6 | import org.junit.Test; 7 | 8 | import java.util.*; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * HtmlReportGeneratorTest 14 | * Created by aqlu on 2016/11/1. 15 | */ 16 | public class HtmlReportGeneratorTest { 17 | @Test 18 | public void generate() throws Exception { 19 | 20 | List aggTestResultList = new ArrayList<>(); 21 | 22 | TestResult testResult1 = TestResult.builder().name("case1").costMills(10).status(TestResult.PASSED).date(Tools.formatDateTimeMills(new Date())).build(); 23 | TestResult testResult2 = TestResult.builder().name("case2").costMills(8).status(TestResult.PASSED).date(Tools.formatDateTimeMills(new Date())).build(); 24 | 25 | TestResult testResult3 = TestResult.builder().name("case3").costMills(5).status(TestResult.FAILED).date(Tools.formatDateTimeMills(new Date())).build().addFailMsgs(Collections.singletonList(new TestResult.FailMsg(2, 1, "equals"))); 26 | TestResult testResult4 = TestResult.builder().name("case4").costMills(12).status(TestResult.ERROR).date(Tools.formatDateTimeMills(new Date())).exception(Tools.getStackTrace(new RuntimeException("人为触发异常")).replaceAll("\n", "
")).build(); 27 | AggTestResult aggTestResult1 = AggTestResult.builder().name("com.qianmi.pc.api.app.product.ProductCountQueryProvider:1.0.0@getStockCount").date(Tools.formatDateTimeMills(new Date())).build().addTestCases(Arrays.asList(testResult1, testResult2, testResult3, testResult4)); 28 | AggTestResult aggTestResult2 = AggTestResult.builder().name("com.qianmi.pc.api.op.item.OpGoodsProvider:1.0.0@batchModifyStock").date(Tools.formatDateTimeMills(new Date())).build().addTestCases(Arrays.asList(testResult1, testResult3)); 29 | 30 | aggTestResultList.add(aggTestResult1); 31 | aggTestResultList.add(aggTestResult2); 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /src/test/java/com/qianmi/tda/TestCaseLoaderTest.java: -------------------------------------------------------------------------------- 1 | package com.qianmi.tda; 2 | 3 | import com.jayway.jsonpath.Option; 4 | import com.jayway.jsonpath.spi.json.JacksonJsonProvider; 5 | import com.jayway.jsonpath.spi.json.JsonProvider; 6 | import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider; 7 | import com.jayway.jsonpath.spi.mapper.MappingProvider; 8 | import com.qianmi.tda.bean.TestSuit; 9 | import com.qianmi.tda.exec.TestCaseLoader; 10 | import org.junit.Before; 11 | import org.junit.Test; 12 | 13 | import java.io.File; 14 | import java.util.EnumSet; 15 | import java.util.List; 16 | import java.util.Set; 17 | 18 | /** 19 | * TestCaseLoaderTest 20 | * Created by aqlu on 2016/10/29. 21 | */ 22 | //@RunWith(SpringRunner.class) 23 | //@SpringBootTest 24 | public class TestCaseLoaderTest { 25 | 26 | // @Autowired 27 | // ApplicationContext ctx; 28 | 29 | @Before 30 | public void setUp() throws Exception { 31 | com.jayway.jsonpath.Configuration.setDefaults(new com.jayway.jsonpath.Configuration.Defaults() { 32 | private final JsonProvider jsonProvider = new JacksonJsonProvider(); 33 | private final MappingProvider mappingProvider = new JacksonMappingProvider(); 34 | 35 | @Override 36 | public JsonProvider jsonProvider() { 37 | return jsonProvider; 38 | } 39 | 40 | @Override 41 | public MappingProvider mappingProvider() { 42 | return mappingProvider; 43 | } 44 | 45 | @Override 46 | public Set