Commit d88f2d46 authored by xie.qin's avatar xie.qin

bug fixed to support sanity test stably.

parent a0dc9565
......@@ -22,11 +22,17 @@ English, please click [here](./HELP.md)
- 复合数据类型 array,需要验证响应消息中是否包含复合类型 object ?是,则需要验证 key 和数据类型;否则仅需要验证数据类型是否匹配期望。
##### 用例参考
[点击此链接](/src/test/resources/features/sanitytest/backend.feature)
- [点击此链接](/src/test/resources/features/sanitytest/backend.feature)
- ![结果截图](/gallery/sanity_test.png)
##### 优点
无需测试人员手动编写测试脚本,测试框架全自动完成 OpenAPI 分析及 REST 消息的发送和结果验证。
##### 局限性
未考虑实际的业务需求;仅限于最基本的可行性验证。
- 无需测试人员手动编写测试脚本,测试框架全自动完成 OpenAPI 分析及 REST 消息的发送和结果验证。
- 根据不同微服务的自我定义,自动适配其支持的协议类型(如,http, https, http2等)进行消息的发送和接收。
- 执行速度快,436个 API,请求和验证可以在40秒内完成。
##### 局限性
- 未考虑实际的业务需求;仅限于最基本的可行性验证。
- 返回失败的请求需要人工二次校验其准确性(如果事先把测试数据准备好,则可规避此问题,但需要付出更多的人力)。
#### 1.1.2 编写符合实际业务需求的 API 测试用例(功能测试)
##### 测试方法
......@@ -43,15 +49,15 @@ English, please click [here](./HELP.md)
- 否则打印错误断言,测试退出并标记为失败。
##### 用例参考
[点击此链接](/src/test/resources/features/apitest/restful/user_management/user_register.feature)
- [点击此链接](/src/test/resources/features/apitest/restful/user_management/user_register.feature)
##### 优点
- 用例编写简单,核心测试脚本仅数行。
- 消息的发送、接收以及结果验证对用例编写人员透明。测试用例编写人员需要关心的只是产品业务需求。
- 测试数据与功能需求关联,支持复用。不同测试脚本(用例)可使用相同测试数据。
- 用例编写简单,核心测试脚本仅数行。
- 消息的发送、接收以及结果验证对用例编写人员透明。测试用例编写人员需要关心的只是产品业务需求。
- 测试数据与功能需求关联,支持复用。不同测试脚本(用例)可使用相同测试数据。
##### 局限性
更多需要讨论的是 API 测试本身的局限性,和本测试框架无关。
- 更多需要讨论的是 API 测试本身的局限性,和本测试框架无关。
#### 1.1.3 构造微服务 mock server,根据不同的请求返回不同的响应结果 (集成测试)
......@@ -45,59 +45,39 @@ public class OpenApiParser {
return openApiFilePath;
}
public Boolean analyzeOpenApiAndGenerateRequests(String serviceName, String targetServiceApiFilePath) {
public void analyzeOpenApiAndGenerateRequests(String serviceName, String targetServiceApiFilePath) throws Throwable {
ObjectMapper objectMapper = new ObjectMapper();
Boolean result = Boolean.TRUE;
try {
JsonNode rootNode = objectMapper.readTree(new File(targetServiceApiFilePath));
JsonNode pathsNode = rootNode.get("paths");
JsonNode definitionsNode = rootNode.get("definitions");
Iterator<Map.Entry<String, JsonNode>> it = pathsNode.fields();
while (it.hasNext()) {
Map.Entry<String, JsonNode> entry = it.next();
result = parseEachPathInOpenApiFile(entry, definitionsNode, serviceName);
}
}
catch (JsonProcessingException e){
e.printStackTrace();
result = Boolean.FALSE;
}
catch (IOException e){
e.printStackTrace();
result = Boolean.FALSE;
JsonNode rootNode = objectMapper.readTree(new File(targetServiceApiFilePath));
JsonNode pathsNode = rootNode.get("paths");
JsonNode definitionsNode = rootNode.get("definitions");
Iterator<Map.Entry<String, JsonNode>> it = pathsNode.fields();
while (it.hasNext()) {
Map.Entry<String, JsonNode> entry = it.next();
parseEachPathInOpenApiFile(entry, definitionsNode, serviceName);
}
return result;
}
public Boolean parseEachPathInOpenApiFile(Map.Entry<String, JsonNode> eachPathNode, JsonNode definitionNode, String serviceName) {
public void parseEachPathInOpenApiFile(Map.Entry<String, JsonNode> eachPathNode, JsonNode definitionNode, String serviceName) {
log.debug("in parsing JsonNode{}", eachPathNode);
Boolean result = Boolean.TRUE;
try{
String path = eachPathNode.getKey();
JsonNode pathNode = eachPathNode.getValue(); //{"post": {}, "get": {}}
Method httpMethod = null;
log.debug("will handle path: {}, its value is {}", path, pathNode);
Iterator<Map.Entry<String, JsonNode>> it = pathNode.fields();
while (it.hasNext()) {
Map.Entry<String, JsonNode> pathValue = it.next();
httpMethod = translateStringToHttpMethod(pathValue.getKey().toLowerCase());
handleEachHttpMethodInOpenApiFile(path, httpMethod, pathValue.getValue(), definitionNode, serviceName);
}
}
catch (Exception e) {
e.printStackTrace();
result = Boolean.FALSE;
String path = eachPathNode.getKey();
JsonNode pathNode = eachPathNode.getValue(); //{"post": {}, "get": {}}
Method httpMethod = null;
log.debug("will handle path: {}, its value is {}", path, pathNode);
Iterator<Map.Entry<String, JsonNode>> it = pathNode.fields();
while (it.hasNext()) {
Map.Entry<String, JsonNode> pathValue = it.next();
httpMethod = translateStringToHttpMethod(pathValue.getKey().toLowerCase());
handleEachHttpMethodInOpenApiFile(path, httpMethod, pathValue.getValue(), definitionNode, serviceName);
}
return result;
}
public void handleEachHttpMethodInOpenApiFile(String URL, Method httpMethod, JsonNode methodNode, JsonNode definitionNode, String serviceName) {
log.debug("in handling HTTP request {} {}", httpMethod, URL);
String path = "";
//parse field consumes
ContentType contentType = null;
ContentType contentType = ContentType.JSON;
if (methodNode.has("consumes")) {
ArrayNode consumesArrayNode = (ArrayNode) methodNode.get("consumes");
for (JsonNode node : consumesArrayNode){
......@@ -108,11 +88,13 @@ public class OpenApiParser {
//get whole path
switch (serviceName.toLowerCase()){
case "backend":
String httpSchema = "http://";
if (backendServiceProperties.getHttpschema() != null && !backendServiceProperties.getHttpschema().isEmpty()){
httpSchema = backendServiceProperties.getHttpschema();
path = backendServiceProperties.getHost() + ":" + backendServiceProperties.getPort() + URL;
if (backendServiceProperties.getHttps()){
path = "https://" + path;
}
else {
path = "http://" + path;
}
path = httpSchema + backendServiceProperties.getHost() + ":" + backendServiceProperties.getPort() + URL;
//batch replace variables in-path
if (path.contains("chain_type")){
path.replace("{chain_type}", globalProperties.getIntmin().toString());
......@@ -120,7 +102,7 @@ public class OpenApiParser {
if (path.matches("\\{.*?\\}")){
path.replaceAll("\\{.*?\\}", globalProperties.getInt32max().toString()); //replaced by 9223372036854775807 ??
}
log.debug("path changed to: {}", path);
log.info("now we're handling REST request: '{} {}'", httpMethod.name(), path);
}
//specify headers. only two conditions: with User-ID, User-Name or without User-ID, User-Name
......@@ -131,6 +113,7 @@ public class OpenApiParser {
//parse field parameters(path, request.json_body)
ArrayNode parametersArrayNode = null;
Map<String, Object> reqJsonBody = new HashMap<String, Object>();
List<Object> reqJsonArrayBody = new ArrayList<>();
/*if(contentType.compareTo(ContentType.JSON) == 0) {
}
else {
......@@ -143,12 +126,30 @@ public class OpenApiParser {
String txtInVal = paraNode.get("in").asText();
//String strInVal = paraNode.get("in").toString();
if (txtInVal.equalsIgnoreCase("query")) {
if (!paraNode.get("type").asText().equalsIgnoreCase("boolean")){ //path must be required
if (!paraNode.has("type") || paraNode.get("type").asText().equalsIgnoreCase("string")){
if (!path.contains("?")){
path = path + "?" + paraNode.get("name").asText() + "=" + globalProperties.getDftstring();
}
else {
path = path + "&" + paraNode.get("name").asText() + "=" + globalProperties.getDftstring();
}
}
else if (paraNode.get("type").asText().equalsIgnoreCase("integer")){ //path must be required
if (!path.contains("?")){
path = path + "?" + paraNode.get("name").asText() + "=" + globalProperties.getInt32max().toString();
if(paraNode.has("format") && paraNode.get("format").asText().equalsIgnoreCase("int64")){
path = path + "?" + paraNode.get("name").asText() + "=" + globalProperties.getInt64max();
}
else {
path = path + "?" + paraNode.get("name").asText() + "=" + globalProperties.getInt32max().toString();
}
}
else{
path = path + "&" + paraNode.get("name").asText() + "=" + globalProperties.getInt32max().toString();
if(paraNode.has("format") && paraNode.get("format").asText().equalsIgnoreCase("int64")){
path = path + "&" + paraNode.get("name").asText() + "=" + globalProperties.getInt64max();
}
else {
path = path + "&" + paraNode.get("name").asText() + "=" + globalProperties.getInt32max().toString();
}
}
}
else{
......@@ -216,31 +217,23 @@ public class OpenApiParser {
if (bodySchema.has("items") && bodySchema.get("items").has("type")){
switch (bodySchema.get("items").get("type").asText()){
case "string":
List<String> tmpStrArr = new ArrayList<>();
tmpStrArr.add(globalProperties.getDftstring());
tmpStrArr.add(globalProperties.getDftstring());
reqJsonBody.put(paraNode.get("name").asText(), tmpStrArr);
reqJsonArrayBody.add(globalProperties.getDftstring());
reqJsonArrayBody.add(globalProperties.getDftstring());
break;
case "integer":
if (bodySchema.get("items").has("format")){
if (bodySchema.get("items").get("format").asText().equalsIgnoreCase("int64")){
List<String> tmpInt64Arr = new ArrayList<>();
tmpInt64Arr.add(globalProperties.getInt64max());
tmpInt64Arr.add(globalProperties.getInt64max());
reqJsonBody.put(paraNode.get("name").asText(), tmpInt64Arr);
reqJsonArrayBody.add(globalProperties.getInt64max());
reqJsonArrayBody.add(globalProperties.getInt64max());
}
else{
List<Integer> tmpInt32Arr = new ArrayList<>();
tmpInt32Arr.add(globalProperties.getInt32max());
tmpInt32Arr.add(globalProperties.getInt32max());
reqJsonBody.put(paraNode.get("name").asText(), tmpInt32Arr);
reqJsonArrayBody.add(globalProperties.getInt32max());
reqJsonArrayBody.add(globalProperties.getInt32max());
}
}
else{
List<Integer> tmpInt32Arr = new ArrayList<>();
tmpInt32Arr.add(globalProperties.getInt32max());
tmpInt32Arr.add(globalProperties.getInt32max());
reqJsonBody.put(paraNode.get("name").asText(), tmpInt32Arr);
reqJsonArrayBody.add(globalProperties.getInt32max());
reqJsonArrayBody.add(globalProperties.getInt32max());
}
break;
default:
......@@ -250,7 +243,7 @@ public class OpenApiParser {
else if(bodySchema.has("items") && bodySchema.get("items").has("originalRef")){
String bodyRef = bodySchema.get("items").get("originalRef").asText();
JsonNode jsonNodeReqBody = definitionNode.get(bodyRef).get("properties");
reqJsonBody = translatePropertyToJsonBody(bodyRef, jsonNodeReqBody, definitionNode);
reqJsonArrayBody.add(translatePropertyToJsonBody(bodyRef, jsonNodeReqBody, definitionNode));
}
else{
log.error("This array structure is not defined in handling scenario. Its structure is: {}", bodySchema.toString());
......@@ -279,11 +272,34 @@ public class OpenApiParser {
reqJsonBody.put("file", binaryStrPath);
}
}
else if (txtInVal.equalsIgnoreCase("path")){
String tmpPathName = paraNode.get("name").asText();
switch(paraNode.get("type").asText().toLowerCase()){
case "boolean":
path = path.replace("{"+tmpPathName+"}", globalProperties.getDftboolean().toString());
break;
case "integer":
if (paraNode.has("format") && paraNode.get("format").asText().equalsIgnoreCase("int64")){
path = path.replace("{"+tmpPathName+"}", globalProperties.getInt64max());
}
else{
path = path.replace("{"+tmpPathName+"}", globalProperties.getInt32max().toString());
}
break;
case "number":
path = path.replace("{"+tmpPathName+"}", globalProperties.getDftnumber().toString());
break;
default:
path = path.replace("{"+tmpPathName+"}", globalProperties.getDftstring());
break;
}
}
}
}
//parse field responses
Map<String, Object> respJsonBody = new HashMap<String, Object>();
List<Object> respJsonArrayBody = new ArrayList<>();
int statusCode = 200;
if (methodNode.has("responses")) {
JsonNode respJsonNode = methodNode.get("responses");
......@@ -308,7 +324,8 @@ public class OpenApiParser {
if (eachRespNodeSchemaValue.get("items").has("originalRef")){
String tmpRef = eachRespNodeSchemaValue.get("items").get("originalRef").asText();
JsonNode jsonNodeRespBody = definitionNode.get(tmpRef).get("properties");
respJsonBody = produceExpectedResponseBody(tmpRef, jsonNodeRespBody, definitionNode); //object array only save 1st element(a object) for matching.
Map<String, Object> tmpArrayObject = produceExpectedResponseBody(tmpRef, jsonNodeRespBody, definitionNode); //object array only save 1st element(a object) for matching.
respJsonArrayBody.add(tmpArrayObject);
/*Map<String, Object> eachEleInRespArr = translatePropertyToJsonBody(jsonNodeRespBody, definitionNode);
//List<Map<String, Object>> mapArrayData = new ArrayList<>();
//mapArrayData.add(eachEleInRespArr);
......@@ -326,7 +343,7 @@ public class OpenApiParser {
}
else if (eachRespNodeSchemaValue.has("type") && eachRespNodeSchemaValue.get("type").asText().equalsIgnoreCase("object") && eachRespNodeSchemaValue.has("additionalProperties")) {
if (eachRespNodeSchemaValue.get("additionalProperties").has("format")){
respJsonBody.put("type", "array.object." + eachRespNodeSchemaValue.get("format").asText().toLowerCase());
respJsonBody.put("type", "array.object." + eachRespNodeSchemaValue.get("additionalProperties").get("format").asText().toLowerCase());
}
else {
respJsonBody.put("type", "array.object." + eachRespNodeSchemaValue.get("type").asText().toLowerCase());
......@@ -371,6 +388,26 @@ public class OpenApiParser {
.respJsonBody(respJsonBody)
.build();
}
else if (!respJsonArrayBody.isEmpty()) {
restfulMsgWithHeaders = RestfulMessageEntity.builder()
.headers(notNullHeaders)
.contentType(contentType)
.method(httpMethod)
.path(path)
.reqJsonBody(reqJsonBody)
.statusCode(statusCode)
.respJsonArrayBody(respJsonArrayBody)
.build();
restfulMsgWithoutHeaders = RestfulMessageEntity.builder()
.contentType(contentType)
.method(httpMethod)
.path(path)
.reqJsonBody(reqJsonBody)
.statusCode(statusCode)
.respJsonArrayBody(respJsonArrayBody)
.build();
}
else {
restfulMsgWithHeaders = RestfulMessageEntity.builder()
.headers(notNullHeaders)
......@@ -392,6 +429,68 @@ public class OpenApiParser {
.build();
}
}
else if (!reqJsonArrayBody.isEmpty()){
if (!respJsonBody.isEmpty()) {
restfulMsgWithHeaders = RestfulMessageEntity.builder()
.headers(notNullHeaders)
.contentType(contentType)
.method(httpMethod)
.path(path)
.reqJsonArrayBody(reqJsonArrayBody)
.statusCode(statusCode)
.respJsonBody(respJsonBody)
.build();
restfulMsgWithoutHeaders = RestfulMessageEntity.builder()
.contentType(contentType)
.method(httpMethod)
.path(path)
.reqJsonArrayBody(reqJsonArrayBody)
.statusCode(statusCode)
.respJsonBody(respJsonBody)
.build();
}
else if (!respJsonArrayBody.isEmpty()){
restfulMsgWithHeaders = RestfulMessageEntity.builder()
.headers(notNullHeaders)
.contentType(contentType)
.method(httpMethod)
.path(path)
.reqJsonArrayBody(reqJsonArrayBody)
.statusCode(statusCode)
.respJsonArrayBody(respJsonArrayBody)
.build();
restfulMsgWithoutHeaders = RestfulMessageEntity.builder()
.contentType(contentType)
.method(httpMethod)
.path(path)
.reqJsonArrayBody(reqJsonArrayBody)
.statusCode(statusCode)
.respJsonArrayBody(respJsonArrayBody)
.build();
}
else {
restfulMsgWithHeaders = RestfulMessageEntity.builder()
.headers(notNullHeaders)
.contentType(contentType)
.method(httpMethod)
.path(path)
.reqJsonArrayBody(reqJsonArrayBody)
.statusCode(statusCode)
//.respJsonBody(respJsonBody)
.build();
restfulMsgWithoutHeaders = RestfulMessageEntity.builder()
.contentType(contentType)
.method(httpMethod)
.path(path)
.reqJsonArrayBody(reqJsonArrayBody)
.statusCode(statusCode)
//.respJsonBody(respJsonBody)
.build();
}
}
else {
if (!respJsonBody.isEmpty()) {
restfulMsgWithHeaders = RestfulMessageEntity.builder()
......@@ -411,6 +510,24 @@ public class OpenApiParser {
.respJsonBody(respJsonBody)
.build();
}
else if (!respJsonArrayBody.isEmpty()) {
restfulMsgWithHeaders = RestfulMessageEntity.builder()
.headers(notNullHeaders)
.contentType(contentType)
.method(httpMethod)
.path(path)
.statusCode(statusCode)
.respJsonArrayBody(respJsonArrayBody)
.build();
restfulMsgWithoutHeaders = RestfulMessageEntity.builder()
.contentType(contentType)
.method(httpMethod)
.path(path)
.statusCode(statusCode)
.respJsonArrayBody(respJsonArrayBody)
.build();
}
else {
restfulMsgWithHeaders = RestfulMessageEntity.builder()
.headers(notNullHeaders)
......@@ -475,7 +592,7 @@ public class OpenApiParser {
return reqJsonBody;
}
if (definitionNode.has(refField) && definitionNode.get(refField).has("properties")){
Map<String, Object> subReqJsonBody = translatePropertyToJsonBody(refField, definitionNode.get(refField), definitionNode);
Map<String, Object> subReqJsonBody = translatePropertyToJsonBody(refField, definitionNode.get(refField).get("properties"), definitionNode);
reqJsonBody.put(property.getKey(), subReqJsonBody);
}
else{
......@@ -547,7 +664,7 @@ public class OpenApiParser {
return reqJsonBody;
}
if (definitionNode.has(refField) && definitionNode.get(refField).has("properties")){
Map<String, Object> subReqJsonBody = translatePropertyToJsonBody(refField, definitionNode.get(refField), definitionNode);
Map<String, Object> subReqJsonBody = translatePropertyToJsonBody(refField, definitionNode.get(refField).get("properties"), definitionNode);
reqJsonBody.put(property.getKey(), subReqJsonBody);
}
else{
......@@ -570,7 +687,7 @@ public class OpenApiParser {
return reqJsonBody;
}
if (definitionNode.has(refField) && definitionNode.get(refField).has("properties")){
Map<String, Object> subReqJsonBody = translatePropertyToJsonBody(refField, definitionNode.get(refField), definitionNode);
Map<String, Object> subReqJsonBody = translatePropertyToJsonBody(refField, definitionNode.get(refField).get("properties"), definitionNode);
reqJsonBody.put(property.getKey(), subReqJsonBody);
}
else{
......@@ -645,8 +762,10 @@ public class OpenApiParser {
return respJsonBody;
}
if (definitionNode.has(refField) && definitionNode.get(refField).has("properties")){
Map<String, Object> subRespJsonBody = produceExpectedResponseBody(refField, definitionNode.get(refField), definitionNode);
respJsonBody.put(property.getKey(), subRespJsonBody);
List<Map<String, Object>> tmpArrayObject = new ArrayList<>();
Map<String, Object> subRespJsonBody = produceExpectedResponseBody(refField, definitionNode.get(refField).get("properties"), definitionNode);
tmpArrayObject.add(subRespJsonBody);
respJsonBody.put(property.getKey(), tmpArrayObject);
}
else{
respJsonBody.put(property.getKey(), "array");
......@@ -674,15 +793,12 @@ public class OpenApiParser {
}
public Boolean produceRestReqWithTestData(JsonNode testReqData, JsonNode testRespData) {
assertThat("There is not destination service in request.", true, equalTo(testReqData.has("service")));
assertThat("There is not destination service in request.", testReqData.has("service"), equalTo(true));
//path
String path = "";
switch (testReqData.get("service").asText().toLowerCase()){
case "backend":
String httpSchema = "http://";
if (backendServiceProperties.getHttpschema() != null && !backendServiceProperties.getHttpschema().isEmpty()){
httpSchema = backendServiceProperties.getHttpschema();
}
path += httpSchema + backendServiceProperties.getHost() + ":" + backendServiceProperties.getPort() + testReqData.get("path").asText();
break;
}
......
......@@ -10,6 +10,7 @@ import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.Map;
@Data
......@@ -27,11 +28,15 @@ public class RestfulMessageEntity {
private Map<String,Object> reqJsonBody;
private List<Object> reqJsonArrayBody;
private JsonNode mapperReqJsonBody;
private int statusCode;
private Map<String,Object> respJsonBody;
private List<Object> respJsonArrayBody;
private JsonNode mapperRespJsonBody;
}
......@@ -2,6 +2,8 @@ package com.fuzamei.autotest.openapi;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fuzamei.autotest.properties.BackendServiceProperties;
import com.fuzamei.autotest.properties.GlobalProperties;
import com.fuzamei.autotest.properties.SpecificProperties;
import io.qameta.allure.restassured.AllureRestAssured;
import io.restassured.RestAssured;
......@@ -11,6 +13,7 @@ import io.restassured.config.RestAssuredConfig;
import io.restassured.http.ContentType;
import io.restassured.mapper.ObjectMapperType;
import io.restassured.path.json.JsonPath;
import io.restassured.path.json.exception.JsonPathException;
import io.restassured.response.Response;
import io.restassured.specification.MultiPartSpecification;
import lombok.extern.slf4j.Slf4j;
......@@ -20,6 +23,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import java.io.File;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.util.*;
......@@ -35,10 +40,28 @@ public class RestfulMessageEventListener {
@Autowired
SpecificProperties specificProperties;
@Autowired
BackendServiceProperties backendServiceProperties;
@Autowired
GlobalProperties globalProperties;
@EventListener
public void processRestfulMessageEvent(RestfulMessageEvent restfulMessageEvent){
log.debug("subscriber received a notification.");
RestfulMessageEntity restfulMessageEntity = restfulMessageEvent.getMsg();
log.debug("subscriber received a message and the message content is {}. ", restfulMessageEntity.toString());
if (backendServiceProperties.getHttpschema().equalsIgnoreCase("http1x")){
processRestMsgEventWithHttp1x(restfulMessageEntity);
}
else{
processRestMsgEventWithHttp2(restfulMessageEntity);
}
}
private void processRestMsgEventWithHttp1x(RestfulMessageEntity restfulMessageEntity) {
final String tmpTarget = restfulMessageEntity.getMethod().name() + " " + restfulMessageEntity.getPath();
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(specificProperties.getRestConnTimeout().intValue())
......@@ -55,7 +78,7 @@ public class RestfulMessageEventListener {
if (restfulMessageEntity.getHeaders() != null) {
if (restfulMessageEntity.getReqJsonBody() != null) {
if (restfulMessageEntity.getContentType().equals(ContentType.MULTIPART)) {
String filePathName = restfulMessageEntity.getRespJsonBody().get("file").toString();
String filePathName = restfulMessageEntity.getReqJsonBody().get("file").toString();
restfulMessageEntity.getReqJsonBody().remove("file");
MultiPartSpecification multiPartSpecBuilder = new MultiPartSpecBuilder(restfulMessageEntity.getReqJsonBody(), ObjectMapperType.JACKSON_2)
......@@ -92,7 +115,8 @@ public class RestfulMessageEventListener {
.extract()
.response();
}
} else if (restfulMessageEntity.getMapperReqJsonBody() != null) {
}
else if (restfulMessageEntity.getMapperReqJsonBody() != null) {
response =
given()
.filter(new AllureRestAssured())
......@@ -107,7 +131,24 @@ public class RestfulMessageEventListener {
//.statusCode(restfulMessageEntity.getStatusCode())
.extract()
.response();
} else {
}
else if (restfulMessageEntity.getReqJsonArrayBody() != null) {
response =
given()
.filter(new AllureRestAssured())
//.config(config)
.body(restfulMessageEntity.getReqJsonArrayBody())
.when()
.contentType(restfulMessageEntity.getContentType())
.headers(restfulMessageEntity.getHeaders())
.request(restfulMessageEntity.getMethod(), restfulMessageEntity.getPath())
.then()
//.assertThat()
//.statusCode(restfulMessageEntity.getStatusCode())
.extract()
.response();
}
else {
response =
given()
.filter(new AllureRestAssured())
......@@ -125,7 +166,7 @@ public class RestfulMessageEventListener {
} else {
if (restfulMessageEntity.getReqJsonBody() != null) {
if (restfulMessageEntity.getContentType().equals(ContentType.MULTIPART)) {
String filePathName = restfulMessageEntity.getRespJsonBody().get("file").toString();
String filePathName = restfulMessageEntity.getReqJsonBody().get("file").toString();
restfulMessageEntity.getReqJsonBody().remove("file");
MultiPartSpecification multiPartSpecBuilder = new MultiPartSpecBuilder(restfulMessageEntity.getReqJsonBody(), ObjectMapperType.JACKSON_2)
......@@ -146,6 +187,8 @@ public class RestfulMessageEventListener {
//.statusCode(restfulMessageEntity.getStatusCode())
.extract()
.response();
final String binaryStrPath = "src" + File.separatorChar + "test" + File.separatorChar + "resources" + File.separatorChar + "testdatacollection" + File.separatorChar + "common" + File.separatorChar + globalProperties.getLatestchain33version() + ".tar.gz";
restfulMessageEntity.getReqJsonBody().put("file", binaryStrPath);
} else {
response =
given()
......@@ -161,7 +204,8 @@ public class RestfulMessageEventListener {
.extract()
.response();
}
} else if (restfulMessageEntity.getMapperReqJsonBody() != null) {
}
else if (restfulMessageEntity.getMapperReqJsonBody() != null) {
response =
given()
.filter(new AllureRestAssured())
......@@ -175,7 +219,23 @@ public class RestfulMessageEventListener {
//.statusCode(restfulMessageEntity.getStatusCode())
.extract()
.response();
} else {
}
else if (restfulMessageEntity.getReqJsonArrayBody() != null) {
response =
given()
.filter(new AllureRestAssured())
//.config(config)
.body(restfulMessageEntity.getReqJsonArrayBody())
.when()
.contentType(restfulMessageEntity.getContentType())
.request(restfulMessageEntity.getMethod(), restfulMessageEntity.getPath())
.then()
//.assertThat()
//.statusCode(restfulMessageEntity.getStatusCode())
.extract()
.response();
}
else {
response =
given()
.filter(new AllureRestAssured())
......@@ -192,96 +252,171 @@ public class RestfulMessageEventListener {
}
}
catch (Exception ex){
//log.error("send rest request failed as error: {}", ex.toString());
log.error("HTTP1.x REST message send failed as error: {}", ex.toString());
ex.printStackTrace();
throw new AssertionError(ex.toString());
throwAssertionErrorInFeatureTest(restfulMessageEntity, ex.toString());
return;
}
try {
log.info("received the response, the status_code is: {}, response json_body is: {}", response.getStatusCode(), response.getBody().asString());
}
catch (JsonPathException jsonPathException){
log.error("Failed to parse json_body from response as error {}", jsonPathException.toString());
throwAssertionErrorInFeatureTest(restfulMessageEntity, jsonPathException.toString());
return;
}
//response format: {code: "baas.err.success", data: {}, message: "OK", status: "OK"}
JsonPath jsonPathEvaluator = null;
assertThat("http status code is not the expectation.", response.getStatusCode(), equalTo(restfulMessageEntity.getStatusCode()));
//check field code in response body
jsonPathEvaluator = response.jsonPath();
String respCode = jsonPathEvaluator.get("code");
assertThat("status code in http response is not the expectation.", respCode, equalTo("baas.err.success"));
try {
assertThat("http status code is not the expectation.", response.getStatusCode(), equalTo(restfulMessageEntity.getStatusCode()));
//check field code in response body
jsonPathEvaluator = response.jsonPath();
String respCode = jsonPathEvaluator.get("code");
assertThat("status code in http response is not the expectation.", respCode, equalTo("baas.err.success"));
}
catch (AssertionError err){
log.error("HTTP1.x REST message [{}] failed to verify status_code. Received [{}], but expected [{}]", tmpTarget, response.getStatusCode(), restfulMessageEntity.getStatusCode());
throwAssertionErrorInFeatureTest(restfulMessageEntity, err.toString());
return;
}
catch (JsonPathException jsonPathException){
log.error("Failed to parse json_body from response as error {}", jsonPathException.toString());
throwAssertionErrorInFeatureTest(restfulMessageEntity, jsonPathException.toString());
return;
}
//response json body only match key, not value
//check field data contains all of needed key
try {
if (restfulMessageEntity.getRespJsonBody() == null && restfulMessageEntity.getRespJsonArrayBody() == null && restfulMessageEntity.getMapperRespJsonBody() == null) {
assertThat("Expected response is null, but received valid data.", jsonPathEvaluator.get("data"), equalTo(null));
return;
}
if (jsonPathEvaluator.get("data") == null) {
assertThat("Expected response is not null, but received null.", true, equalTo(
restfulMessageEntity.getRespJsonBody() != null ||
restfulMessageEntity.getRespJsonArrayBody() != null ||
restfulMessageEntity.getMapperRespJsonBody() != null));
return;
}
}
catch (AssertionError err){
log.error("HTTP1.x REST message [{}] failed to verify response_body. Received is [{}], but expected is not.", tmpTarget, jsonPathEvaluator.get("data"));
throwAssertionErrorInFeatureTest(restfulMessageEntity, err.toString());
return;
}
try{
//object
Map<String, Object> objRespBody = new HashMap<>();
if (jsonPathEvaluator != null) {
objRespBody = jsonPathEvaluator.getMap("data");
if (restfulMessageEntity.getMapperRespJsonBody() != null) {
matchKeyAndDataValueInRespWithExpectation(objRespBody, restfulMessageEntity.getMapperRespJsonBody());
}
else {
objRespBody = jsonPathEvaluator.getMap("data");
log.debug("response json_body is a object. its value is: {}", objRespBody);
//no need to assert expected response data type is map here as expected response is a object {"type": expected_data_type}
if (restfulMessageEntity.getMapperRespJsonBody() != null) {
matchKeyAndDataValueInRespWithExpectation(objRespBody, restfulMessageEntity.getMapperRespJsonBody());
}
else {
try {
matchKeyAndDataTypeInRespWithExpectation(objRespBody, restfulMessageEntity.getRespJsonBody());
}
catch (AssertionError error){
log.error("HTTP1.x REST message [{}] failed to verify response_body. Received is [{}], but expected is [{}].", tmpTarget, objRespBody, restfulMessageEntity.getRespJsonBody());
throwAssertionErrorInFeatureTest(restfulMessageEntity, error.toString());
return;
}
}
}
catch (ClassCastException e){
try{
//array
List<Map<String, Object>> arrRespBody = new ArrayList<>();
List<Object> arrRespBody = new ArrayList<>();
arrRespBody = jsonPathEvaluator.getList("data");
log.debug("response json_body is a array, its value is: {}", arrRespBody);
//objects in array
try{
Map<String, Object> arrEleRespBody = arrRespBody.get(0);
matchKeyAndDataTypeInRespWithExpectation(arrEleRespBody, restfulMessageEntity.getRespJsonBody());
if (arrRespBody.size() > 0) {
Map<String, Object> arrEleRespBody = (Map<String, Object>) arrRespBody.get(0);
if (restfulMessageEntity.getRespJsonArrayBody() != null) {
matchKeyAndDataTypeInRespWithExpectation(arrEleRespBody, (Map<String, Object>) restfulMessageEntity.getRespJsonArrayBody().get(0));
}
else if (restfulMessageEntity.getMapperRespJsonBody() != null) {
matchKeyAndDataValueInRespWithExpectation(arrEleRespBody, (ArrayNode)restfulMessageEntity.getMapperRespJsonBody().get(0));
}
else {
//not available behaviour.
matchKeyAndDataTypeInRespWithExpectation(arrEleRespBody, restfulMessageEntity.getRespJsonBody());
}
}
else {
assertThat("Received data type does not match the expectation.", true, equalTo(arrRespBody instanceof ArrayList<?>));
}
}catch (ClassCastException exception){
//normal array, not objects in array
String expectedDataType = restfulMessageEntity.getRespJsonBody().get("type").toString();
matchDataTypeInRespWithExpectation(arrRespBody.get(0), expectedDataType);
try {
String expectedDataType = restfulMessageEntity.getRespJsonBody().get("type").toString();
matchDataTypeInRespWithExpectation(arrRespBody.get(0), expectedDataType);
}
catch (AssertionError error){
log.error("HTTP1.x REST message [{}] failed to verify data type in response_body array. Received is [{}], but expected is [{}].", tmpTarget, arrRespBody.get(0), restfulMessageEntity.getRespJsonBody().get("type").toString());
throwAssertionErrorInFeatureTest(restfulMessageEntity, error.toString());
return;
}
}
catch (AssertionError error){
log.error("HTTP1.x REST message [{}] failed to verify array type response_body. Received is [{}], but expected is [{}].", tmpTarget, jsonPathEvaluator.get("data"), restfulMessageEntity);
throwAssertionErrorInFeatureTest(restfulMessageEntity, error.toString());
return;
}
}catch (ClassCastException ex){
//neither object, nor array
if (restfulMessageEntity.getMapperRespJsonBody() != null){
String actual = jsonPathEvaluator.get("data").toString();
String expected = restfulMessageEntity.getMapperRespJsonBody().asText();
assertThat("Received data does not match the expectation.", actual.equals(expected), equalTo(true));
try {
if (restfulMessageEntity.getMapperRespJsonBody() != null) {
String actual = jsonPathEvaluator.get("data").toString();
String expected = restfulMessageEntity.getMapperRespJsonBody().asText();
assertThat("Received data does not match the expectation.", actual.equals(expected), equalTo(true));
} else if (restfulMessageEntity.getRespJsonBody() != null) {
String expectedDataType = restfulMessageEntity.getRespJsonBody().get("type").toString();
matchDataTypeInRespWithExpectation(jsonPathEvaluator.get("data"), expectedDataType);
}
}
else {
String expectedDataType = restfulMessageEntity.getRespJsonBody().get("type").toString();
matchDataTypeInRespWithExpectation(jsonPathEvaluator.get("data"), expectedDataType);
catch (AssertionError error){
log.error("HTTP1.x REST message [{}] failed to verify data type of response_body. Received is [{}], but expected is [{}].", tmpTarget, jsonPathEvaluator.get("data"), restfulMessageEntity);
throwAssertionErrorInFeatureTest(restfulMessageEntity, error.toString());
return;
}
}
}
}
private void processRestMsgEventWithHttp2(RestfulMessageEntity restfulMessageEntity) {
}
private void matchKeyAndDataTypeInRespWithExpectation(Map<String, Object> recvRespBody, Map<String, Object> expectedRespBody) {
//how to match: traverse expectedRespBody and found corresponding key in recvRespBody
for (Map.Entry<String, Object> entry : expectedRespBody.entrySet()) {
log.debug("traversing expectedRespBody now, the key is {}", entry.getKey());
try{
//to match key
assertThat("Cannot found out same key in Response Body.", true, equalTo(recvRespBody.containsKey(entry.getKey())));
//to match type of value
if(entry.getValue() instanceof Map){
matchKeyAndDataTypeInRespWithExpectation((Map<String, Object>) recvRespBody.get(entry.getKey()), (Map<String, Object>) entry.getValue());
}
else if (entry.getValue() instanceof Map[]){
List<Map<String, Object>> recv = (List<Map<String, Object>>)recvRespBody.get(entry.getKey());
Map<String, Object> matchRecv = recv.get(0);
log.debug("traversing expectedRespBody now, the key is [{}]", entry.getKey());
//to match key
assertThat("Cannot found out same key in Response Body.", true, equalTo(recvRespBody.containsKey(entry.getKey())));
//to match type of value
if (entry.getValue() instanceof Map) {
matchKeyAndDataTypeInRespWithExpectation((Map<String, Object>) recvRespBody.get(entry.getKey()), (Map<String, Object>) entry.getValue());
} else if (entry.getValue() instanceof Map[]) {
List<Map<String, Object>> recv = (List<Map<String, Object>>) recvRespBody.get(entry.getKey());
Map<String, Object> matchRecv = recv.get(0);
List<Map<String, Object>> expected = (List<Map<String, Object>>)entry.getValue();
Map<String, Object> matchExpected = expected.get(0);
List<Map<String, Object>> expected = (List<Map<String, Object>>) entry.getValue();
Map<String, Object> matchExpected = expected.get(0);
matchKeyAndDataTypeInRespWithExpectation(matchRecv, matchExpected);
matchKeyAndDataTypeInRespWithExpectation(matchRecv, matchExpected);
} else if (entry.getValue().getClass().isArray()) {
assertThat("Received data type is not array", recvRespBody.get(entry.getKey()).getClass().isArray(), equalTo(true));
if (Array.getLength(recvRespBody.get(entry.getKey())) > 0) {
int tmpArrayLength = Array.getLength(recvRespBody.get(entry.getKey()));
matchDataTypeInRespWithExpectation(Array.get(recvRespBody.get(entry.getKey()), 0), Array.get(entry.getValue(), 0).toString());
}
else {
try {
matchDataTypeInRespWithExpectation(recvRespBody.get(entry.getKey()), entry.getValue().toString());
}catch (AssertionError error){
log.error("After matched, the key:{}'s data type does not match the expectation.", entry.getKey());
break;
}
}
}
catch (AssertionError e){
log.error("After matched, the key {} missed in received response message.", entry.getKey());
break;
} else {
matchDataTypeInRespWithExpectation(recvRespBody.get(entry.getKey()), entry.getValue().toString());
}
}
......@@ -292,7 +427,7 @@ public class RestfulMessageEventListener {
Iterator<Map.Entry<String, JsonNode>> it = expectedRespBody.fields();
while (it.hasNext()) {
Map.Entry<String, JsonNode> entry = it.next();
log.debug("traversing expectedRespBody now, the key is {}", entry.getKey());
log.debug("traversing expectedRespBody now, the key is [{}]", entry.getKey());
//to match key
assertThat("Cannot found out same key in Response Body.", true, equalTo(recvRespBody.containsKey(entry.getKey())));
......@@ -370,6 +505,19 @@ public class RestfulMessageEventListener {
break;
}
assertThat("response data type cannot match the expectation.", matched, equalTo(true));
/*try {
assertThat("response data type cannot match the expectation.", matched, equalTo(true));
}
catch (AssertionError error){
log.error("failed to match data type [{}]. Expected data type is [{}]", recvDataType, expectedDataType);
throw new AssertionError(error.toString());
}*/
}
private void throwAssertionErrorInFeatureTest(RestfulMessageEntity restfulMessageEntity, String errMsg){
if (restfulMessageEntity.getMapperReqJsonBody() != null || restfulMessageEntity.getMapperRespJsonBody() != null){
throw new AssertionError(errMsg);
}
}
}
......@@ -16,4 +16,5 @@ public class BackendServiceProperties {
private String port;
private String openapifilepath;
private String httpschema;
private Boolean https;
}
......@@ -14,9 +14,12 @@ server.port=8080
logging.level.root=WARN
logging.level.org.springframework=ERROR
logging.level.com.fuzamei.autotest=DEBUG
logging.config=classpath:logback-spring.xml
spring.jackson.default-property-inclusion=non_null
service.backend.httpschema=http://
service.backend.https=false
service.backend.httpschema=http1x
#service.backend.httpschema=http2
service.backend.host=172.22.18.152
service.backend.port=2345
service.backend.openapidefinitionpath=/
......@@ -32,6 +35,6 @@ spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
mybatis.type-aliases-package=com.fuzamei.autotest.testdata.entity
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
restassured.connecttimeout=3000
restassured.requesttimeout=3000
restassured.sockettimeout=3000
\ No newline at end of file
restassured.connecttimeout=10000
restassured.requesttimeout=10000
restassured.sockettimeout=10000
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时,配置文档如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文档是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。
当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="10 seconds">
<contextName>logback-spring</contextName>
<!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义后,可以使“${}”来使用变量。 -->
<property name="logging.path" value="./target/cucumber/log" />
<!--0. 日志格式和颜色渲染 -->
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!--1. 输出到控制台-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
<encoder>
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!--2. 输出到文档-->
<!-- 2.1 level为 DEBUG 日志,时间滚动输出 -->
<appender name="CUSTOM_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${logging.path}/autotest_custom.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志归档 -->
<fileNamePattern>${logging.path}/autotest-custom-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文档保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文档只记录debug级别的 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
</appender>
<!-- 2.2 level为 INFO 日志,时间滚动输出 -->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${logging.path}/autotest_info.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天日志归档路径以及格式 -->
<fileNamePattern>${logging.path}/autotest-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文档保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文档只记录info级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>info</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 2.3 level为 WARN 日志,时间滚动输出 -->
<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${logging.path}/autotest_warn.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${logging.path}/autotest-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文档保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文档只记录warn级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>warn</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 2.4 level为 ERROR 日志,时间滚动输出 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${logging.path}/autotest_error.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${logging.path}/web-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文档保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文档只记录ERROR级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!--
<logger>用来设置某一个包或者具体的某一个类的日志打印级别、
以及指定<appender>。<logger>仅有一个name属性,
一个可选的level和一个可选的addtivity属性。
name:用来指定受此logger约束的某一个包或者具体的某一个类。
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。
如果未设置此属性,那么当前logger将会继承上级的级别。
addtivity:是否向上级logger传递打印信息。默认是true。
<logger name="org.springframework.web" level="info"/>
<logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/>
-->
<!--
使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作:
第一种把<root level="info">改成<root level="DEBUG">这样就会打印sql,不过这样日志那边会出现很多其他消息
第二种就是单独给dao下目录配置debug模式,代码如下,这样配置sql语句会打印,其他还是正常info级别:
【logging.level.org.mybatis=debug logging.level.dao=debug】
-->
<!--
root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
不能设置为INHERITED或者同义词NULL。默认是DEBUG
可以包含零个或多个元素,标识这个appender将会添加到这个logger。
-->
<!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
<logger name="org.springframework" level="WARN"></logger>
<logger name="org.mybatis" level="WARN"></logger>
<logger name="org.apache.zookeeper" level="WARN"></logger>
<!-- 4. 最终的策略 -->
<!-- 4.1 开发环境:打印控制台-->
<springProfile name="dev">
<logger name="com.fuzamei.autotest" level="debug"/><!-- 修改此处扫描包名 -->
</springProfile>
<root level="debug">
<appender-ref ref="CONSOLE" />
<appender-ref ref="CUSTOM_FILE" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="WARN_FILE" />
<appender-ref ref="ERROR_FILE" />
</root>
4.2 生产环境:输出到文档
<springProfile name="pro">
<root level="info">
<appender-ref ref="CONSOLE" />
<appender-ref ref="CUSTOM_FILE" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="ERROR_FILE" />
<appender-ref ref="WARN_FILE" />
</root>
</springProfile>
</configuration>
......@@ -55,7 +55,7 @@ public class ApiVerification {
default:
break;
}
assertThat(openApiParser.analyzeOpenApiAndGenerateRequests(serviceName, targetServiceApiFilePath), is(Boolean.TRUE));
openApiParser.analyzeOpenApiAndGenerateRequests(serviceName, targetServiceApiFilePath);
}
@Then("^Send RESTful request with dataNum (.*?) to verify interface .*?$")
......
......@@ -4,4 +4,4 @@ Feature: API tests for service of backend
Scenario: Verify the exposed APIs work as design
Given Found OpenAPI definition for backend service
When The testing PERSISTENCE data orgUser is ready for backend service
#Then Analyze OpenAPI file of backend service and generate REST-ful requests automatically
\ No newline at end of file
Then Analyze OpenAPI file of backend service and generate REST-ful requests automatically
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment