Commit 35ed4e0f authored by xie.qin's avatar xie.qin

firstly commit.

parent 9534500f
HELP.md
build/
gradle
!gradle/wrapper/gradle-wrapper.jar
!gradle/wrapper/gradle-wrapper.properties
!**/src/main/**/build/
!**/src/test/**/build/
......@@ -34,3 +36,6 @@ out/
### VS Code ###
.vscode/
/gradle/wrapper/wrapper/
/gradle/
/gradle/wrapper/wrapper/dists/
# Automation Test Framework for Automatic API and Java-SDK Testing
## Automatic API Testing
Depend on Open API technology, This test framework helps you to verify the correctness and compliance of micro-service's exposure APIs.
Especially, it gives great supports in regression tests.
# Automation Test Framework for REST-ful API and Java-SDK Testing
## REST-ful API Testing
Supported by Open API technology, This test framework helps you to verify the correctness and compliance of micro-service's exposure APIs.
Especially, it gives great supports and decreases too much efforts in regression tests .
Assumed we have a simple software product and it has following business call flow:
```FrontEnd_Svc ---- <RESTful Req1> ---- BackEnd_Svc1 ---- <RESTful Req2 which triggered by RESTful Req1> ---- BackEnd_Svc2```
If we have the predefined RESTful API of BackEnd_Svc1 & BackEnd_Svc2, then much repetitive end-to-end verification efforts can be saved.
If we have the predefined REST-ful API of BackEnd_Svc1 & BackEnd_Svc2, then much repetitive end-to-end verification efforts can be saved.
Automation API test consists following 2 parts:
1. Verify the exposed APIs work as design in BackEnd_Svc1 and BackEnd_Svc2
* To have this test framework generated RESTful requests automatically based on API definition.
* To have this test framework generated REST-ful requests automatically based on API definition.
Note:
- For easy to use and test efficiency, except user and organization info, no need to persist more testing data.
- So in the expectation of automation test, GET/PUT method request with a specific ID (it is the maximum value based on the data type) will be failed perhaps, but most of POST method requests should succeed.
- For those optional query parameters in path, requests will be sent out twice. One is with them, another is without them.
- Each field in Json body of request will use the boundary values based on data type.
* After received the response, it will check HTTP status code and json_body with above API definition also.
* Ont only verify the positive call flows, it can generate some negative test cases automatically depend on "parameters" type (e.g. over the boundary).
2. Similarly, we can construct some HTTP servers as mocks when the BackEnd_Svcs are still in developing.
With it, we can verify business logic of FrontEnd_Svc OR triggered behaviour in BackEnd_Svc1, not to care about development progress of Back1End_Svc1 OR BackEnd_Svc2.
\ No newline at end of file
* With it, we can verify business logic of FrontEnd_Svc OR triggered behaviour in BackEnd_Svc1, not to care about development progress of Back1End_Svc1 OR BackEnd_Svc2.
3. Limitation
* All automatically generated requests does not consider business logic. So the assert failure is not real failure, manually double check is preferred.
## Java SDK Testing
\ No newline at end of file
package com.fuzamei.autotest.openapi;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fuzamei.autotest.properties.BackendServiceProperties;
import io.qameta.allure.restassured.AllureRestAssured;
import io.restassured.http.ContentType;
import io.restassured.http.Header;
import io.restassured.http.Headers;
import io.restassured.http.Method;
import io.restassured.response.Response;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
@Slf4j
@Component
public class ParseOpenApi {
@Autowired
BackendServiceProperties backendServiceProperties;
@Resource
private ApplicationContext applicationContext;
private String classPath = this.getClass().getClassLoader().getResource("/").getPath();
public String getOpenApiFilePathByVersion(String serviceName, String testRelease) {
log.debug("classPath is {}", classPath);
String openApiFilePath = "";
openApiFilePath = classPath + File.separator +
"testDataCollection" + File.separator +
testRelease + File.separator +
serviceName + "OpenAPI.json";
log.debug("openApiFilePath is {}", openApiFilePath);
File file = new File(openApiFilePath);
if (!file.exists()){
openApiFilePath = "";
}
return openApiFilePath;
}
public Boolean analyzeOpenApiAndGenerateRequests(String serviceName, String targetServiceApiFilePath) {
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;
}
return result;
}
public Boolean 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();
switch (pathValue.getKey().toLowerCase()){
case "get":
httpMethod = Method.GET;
break;
case "post":
httpMethod = Method.POST;
break;
case "put":
httpMethod = Method.PUT;
break;
case "delete":
httpMethod = Method.DELETE;
break;
case "patch":
httpMethod = Method.PATCH;
break;
}
handleEachHttpMethodInOpenApiFile(path, httpMethod, pathValue.getValue(), definitionNode, serviceName);
}
}
catch (Exception e) {
e.printStackTrace();
result = Boolean.FALSE;
}
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;
if (methodNode.has("consumes")) {
ArrayNode consumesArrayNode = (ArrayNode) methodNode.get("consumes");
for (JsonNode node : consumesArrayNode){
if (node.toString().contains("octet")){
contentType = ContentType.BINARY;
}
else if (node.toString().contains("xml")){
contentType = ContentType.XML;
}
else if (node.toString().contains("json") || node.toString().contains("javascript")){
contentType = ContentType.JSON;
}
else if (node.toString().contains("multipart")){
contentType = ContentType.MULTIPART;
}
else if (node.toString().contains("plain")){
contentType = ContentType.TEXT;
}
else if (node.toString().contains("urlencoded")){
contentType = ContentType.URLENC;
}
else{
contentType = ContentType.HTML;
}
break;
}
}
//get whole path
switch (serviceName.toLowerCase()){
case "backend":
String httpSchema = "http://";
if (backendServiceProperties.getHttpSchema() != null && !backendServiceProperties.getHttpSchema().isEmpty()){
httpSchema = backendServiceProperties.getHttpSchema();
}
path = httpSchema + backendServiceProperties.getHost() + ":" + backendServiceProperties.getPort() + URL;
//batch replace variables in-path
if (path.contains("chain_type")){
path.replace("{chain_type}", "0");
}
if (path.matches("\\{.*?\\}")){
path.replaceAll("\\{.*?\\}", "87654321"); //replaced by 9223372036854775807 ??
}
log.debug("path changed to: {}", path);
}
//specify headers. only two conditions: with User-ID, User-Name or without User-ID, User-Name
Header userID = new Header("User-ID", "87654321");
Header userName = new Header("User-Name", "automation");
Headers notNullHeaders = new Headers(userID, userName);
//parse field parameters
ArrayNode parametersArrayNode = null;
Map<String, Object> reqJsonBody = new HashMap<String, Object>();
if (methodNode.has("parameters")) {
parametersArrayNode = (ArrayNode) methodNode.get("parameters");
for (JsonNode paraNode : parametersArrayNode){
if (paraNode.get("in").toString().equalsIgnoreCase("query")) {
if (!paraNode.get("type").toString().equalsIgnoreCase("boolean")){ //path must be required
if (!path.contains("?")){
path = path + "?" + paraNode.get("name").toString() + "=87654321";
}
else{
path = path + "&" + paraNode.get("name").toString() + "=87654321";
}
}
else{
if (!path.contains("?")){
path = path + "?" + paraNode.get("name").toString() + "=false";
}
else{
path = path + "&" + paraNode.get("name").toString() + "=false";
}
}
}
else if (paraNode.get("in").toString().equalsIgnoreCase("body")){
JsonNode bodySchema = paraNode.get("schema");
if (bodySchema.has("originalRef")){
String bodyRef = bodySchema.get("originalRef").toString();
log.debug("get json_body from ref /definitions/{}", bodyRef);
JsonNode jsonNodeReqBody = definitionNode.get(bodyRef).get("properties");
reqJsonBody = translatePropertyToJsonBody(jsonNodeReqBody, definitionNode);
}
}
}
}
//parse field responses
Map<String, Object> respJsonBody = new HashMap<String, Object>();
int statusCode = 200;
if (methodNode.has("response")) {
JsonNode respJsonNode = methodNode.get("response");
Iterator<Map.Entry<String, JsonNode>> it = respJsonNode.fields();
while (it.hasNext()){
Map.Entry<String, JsonNode> respNodeValue = it.next();
if (respNodeValue.getValue().has("schema")){
statusCode = Integer.parseInt(respNodeValue.getKey());
JsonNode eachRespNodeValue = respNodeValue.getValue().get("schema");
//"200": {"description": "OK", "schema": { "$ref": "#/definitions/IPage«BaseChainResponse»", "originalRef": "IPage«BaseChainResponse»" }}
if (eachRespNodeValue.has("originalRef")){ //type="object" and it has properties' detail info
String tmpRef = eachRespNodeValue.get("originalRef").toString();
JsonNode jsonNodeRespBody = definitionNode.get(tmpRef).get("properties");
respJsonBody = translatePropertyToJsonBody(jsonNodeRespBody, definitionNode);
}
else if (eachRespNodeValue.has("type") && eachRespNodeValue.get("type").toString().equalsIgnoreCase("array")) { //type="array"
/**
"200": {"description": "OK", "schema": {"type": "array", "items": {"type": "object"}}}
"200": {"description": "OK", "schema": {"type": "array","items": {"$ref": "#/definitions/SysDict对象","originalRef": "SysDict对象" }}}
*/
}
else{
//type="string, integer, boolean, number"
//"200": {"description": "OK", "schema": { "type": "boolean" } }
respJsonBody.put("type", eachRespNodeValue.get("schema").get("type").toString().toLowerCase());
}
break;
}
}
}
RestfulMessage restfulMsgWithHeaders = null;
RestfulMessage restfulMsgWithoutHeaders = null;
if (!reqJsonBody.isEmpty() ) {
restfulMsgWithHeaders = RestfulMessage.builder()
.headers(notNullHeaders)
.contentType(contentType)
.method(httpMethod)
.path(path)
.reqJsonBody(reqJsonBody)
.statusCode(statusCode)
.respJsonBody(respJsonBody)
.build();
restfulMsgWithoutHeaders = RestfulMessage.builder()
.contentType(contentType)
.method(httpMethod)
.path(path)
.reqJsonBody(reqJsonBody)
.statusCode(statusCode)
.respJsonBody(respJsonBody)
.build();
}
else {
restfulMsgWithHeaders = RestfulMessage.builder()
.headers(notNullHeaders)
.contentType(contentType)
.method(httpMethod)
.path(path)
.statusCode(statusCode)
.respJsonBody(respJsonBody)
.build();
restfulMsgWithoutHeaders = RestfulMessage.builder()
.contentType(contentType)
.method(httpMethod)
.path(path)
.statusCode(statusCode)
.respJsonBody(respJsonBody)
.build();
}
//publish message and wait subscriber(e.g. rest-assure) to send out request
RestfulMessageEvent restfulMessageEvent = new RestfulMessageEvent(this);
restfulMessageEvent.setMsg(restfulMsgWithHeaders);
applicationContext.publishEvent(restfulMessageEvent);
restfulMessageEvent.setMsg(restfulMsgWithoutHeaders);
applicationContext.publishEvent(restfulMessageEvent);
}
private Map<String, Object> translatePropertyToJsonBody(JsonNode jsonNodeReqBody, JsonNode definitionNode) {
Map<String, Object> reqJsonBody = new HashMap<String, Object>();
Iterator<Map.Entry<String, JsonNode>> it = jsonNodeReqBody.fields();
while (it.hasNext()) {
Map.Entry<String, JsonNode> property = it.next();
//"accountId": {"type": "integer", "format": "int64", "description": "账户id"}
if (property.getValue().has("type")){
switch (property.getValue().get("type").toString().toLowerCase()){
case "integer":
if (property.getValue().get("format").toString().equalsIgnoreCase("int32")){
reqJsonBody.put(property.getKey(), 2147483647);
}
else{
reqJsonBody.put(property.getKey(), 9223372036854775807L);
}
break;
case "string":
reqJsonBody.put(property.getKey(), "1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy1J7mdg5rbQyUHENYdx39WVWK7fsLpE");
break;
case "number":
BigDecimal bigDecimal = new BigDecimal(9999999999.99);
reqJsonBody.put(property.getKey(), bigDecimal);
break;
case "boolean":
reqJsonBody.put(property.getKey(), true);
break;
case "object":
if (property.getValue().has("additionalProperties") && property.getValue().get("additionalProperties").has("originalRef")){
//"attributes":{"type": "object", "description": "商品属性信息", "additionalProperties": {"originalRef": "商品属性信息"}}
String refField = property.getValue().get("additionalProperties").get("originalRef").toString();
if (definitionNode.has(refField) && definitionNode.get(refField).has("properties")){
Map<String, Object> subReqJsonBody = translatePropertyToJsonBody(definitionNode.get(refField), definitionNode);
reqJsonBody.put(property.getKey(), subReqJsonBody);
}
else{
reqJsonBody.put(property.getKey(), "{}");
}
}
else{
//"details": {"type": "object", "description": "价格明细", "additionalProperties": {"type": "number"}
reqJsonBody.put(property.getKey(), "{}");
}
break;
case "array":
if (property.getValue().has("items")){
//"param": {"type": "array","description": "合约调用参数,用于合约调用","items": {"type": "object"}}
if (property.getValue().get("items").has("type") && !property.getValue().get("items").has("originalRef")){
switch (property.getValue().get("items").get("type").toString().toLowerCase()){
case "integer":
if (property.getValue().get("items").has("format") && property.getValue().get("items").get("format").toString().equalsIgnoreCase("int64")){
List<Long> longArrayData = new ArrayList<>();
longArrayData.add(9223372036854775807L);
longArrayData.add(9223372036854775807L);
reqJsonBody.put(property.getKey(), longArrayData);
}
else{
List<Integer> intArrayData = new ArrayList<>();
intArrayData.add(2147483647);
intArrayData.add(2147483647);
reqJsonBody.put(property.getKey(), intArrayData);
}
break;
case "string":
List<String> strArrayData = new ArrayList<>();
strArrayData.add("1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy1J7mdg5rbQyUHENYdx39WVWK7fsLpE");
strArrayData.add("1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy1J7mdg5rbQyUHENYdx39WVWK7fsLpE");
reqJsonBody.put(property.getKey(), strArrayData);
break;
case "boolean":
List<Boolean> boolArrayData = new ArrayList<>();
boolArrayData.add(false);
boolArrayData.add(true);
reqJsonBody.put(property.getKey(), boolArrayData);
break;
case "number":
List<BigDecimal> bigDecimalsArrayData = new ArrayList<>();
bigDecimalsArrayData.add(new BigDecimal(9999999999.99));
bigDecimalsArrayData.add(new BigDecimal(9999999999.99));
reqJsonBody.put(property.getKey(), bigDecimalsArrayData);
break;
case "object":
reqJsonBody.put(property.getKey(), "[]");
break;
}
}
//"nodes": {"type": "array", "description": "节点列表", "items": { "$ref": "#/definitions/ChainNodeRequest","originalRef": "ChainNodeRequest"}}
else {
String refField = property.getValue().get("items").get("originalRef").toString();
if (definitionNode.has(refField) && definitionNode.get(refField).has("properties")){
Map<String, Object> subReqJsonBody = translatePropertyToJsonBody(definitionNode.get(refField), definitionNode);
reqJsonBody.put(property.getKey(), subReqJsonBody);
}
else{
reqJsonBody.put(property.getKey(), "[]");
}
}
}
break;
}
}
//"chainExplorerNodeRequest": {"description": "浏览器部署节点信息","$ref": "#/definitions/ChainExplorerNodeRequest","originalRef": "ChainExplorerNodeRequest"}
else if(property.getValue().has("originalRef")) {
String refField = property.getValue().get("originalRef").toString();
if (definitionNode.has(refField) && definitionNode.get(refField).has("properties")){
Map<String, Object> subReqJsonBody = translatePropertyToJsonBody(definitionNode.get(refField), definitionNode);
reqJsonBody.put(property.getKey(), subReqJsonBody);
}
else{
reqJsonBody.put(property.getKey(), "{}");
}
}
}
return reqJsonBody;
}
}
......@@ -15,7 +15,7 @@ import java.util.Map;
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RestfulMessageModel {
public class RestfulMessage {
private ContentType contentType;
private Headers headers;
......@@ -24,7 +24,9 @@ public class RestfulMessageModel {
private String path;
private Map<String,Object> jsonBody;
private Map<String,Object> reqJsonBody;
private Response response;
private int statusCode;
private Map<String,Object> respJsonBody;
}
package com.fuzamei.autotest.openapi;
public class RestfulMessageEvent {
import org.springframework.context.ApplicationEvent;
public class RestfulMessageEvent extends ApplicationEvent {
private RestfulMessage restfulMsg;
public RestfulMessageEvent(Object source) {
super(source);
}
public RestfulMessage getMsg() {
return restfulMsg;
}
public void setMsg(RestfulMessage restfulMsg) {
this.restfulMsg = restfulMsg;
}
}
package com.fuzamei.autotest.openapi;
import io.qameta.allure.restassured.AllureRestAssured;
import io.restassured.path.json.JsonPath;
import io.restassured.response.Response;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
@Component
public class RestfulMessageHandler {
@Slf4j
public class RestfulMessageEventListener {
@EventListener
public void processRestfulMessageEvent(RestfulMessageEvent restfulMessageEvent){
log.debug("subscriber received a notification.");
RestfulMessage restfulMessage = restfulMessageEvent.getMsg();
Response response = given()
.filter(new AllureRestAssured())
.when()
.contentType(restfulMessage.getContentType())
.headers(restfulMessage.getHeaders())
.request(restfulMessage.getMethod(), restfulMessage.getPath())
.then()
.assertThat()
.statusCode(restfulMessage.getStatusCode())
.extract()
.response();
//response format: {code: "baas.err.success", data: {}, message: "OK", status: "OK"}
//assertThat("http status code is not the expectation.", response.getStatusCode(), equalTo(restfulMessage.getStatusCode()));
//check field code in response body
JsonPath jsonPathEvaluator = response.jsonPath();
String respCode = jsonPathEvaluator.get("code");
assertThat("status code in http response is not the expectation.", respCode, equalTo("baas.err.success"));
//response json body only match key, not value
//check field data contains all of needed key
try{
Map<String, Object> respBody = new HashMap<>();
respBody = jsonPathEvaluator.getMap("data");
matchKeyInRespWithExpectation(respBody, restfulMessage.getRespJsonBody());
}
catch (ClassCastException e){
String expectedDataType = restfulMessage.getRespJsonBody().get("type").toString();
matchDataTypeInRespWithExpectation(jsonPathEvaluator.get("data"), expectedDataType);
}
}
/**
"200": {"description": "OK", "schema": {"type": "array", "items": {"type": "object"}}}
"200": {"description": "OK", "schema": {"type": "array","items": {"$ref": "#/definitions/SysDict对象","originalRef": "SysDict对象" }}}
*/
private void matchKeyInRespWithExpectation(Map<String, Object> recvRespBody, Map<String, Object> expectedRespBody) {
}
private void matchDataTypeInRespWithExpectation(Object recvDataType, String expectedDataType) {
boolean matched = false;
switch (expectedDataType) {
case "string":
if (recvDataType instanceof String){
matched = true;
}
break;
case "boolean":
if (recvDataType instanceof Boolean){
matched = true;
}
break;
case "integer":
if (recvDataType instanceof Integer) {
matched = true;
}
break;
case "number":
if (recvDataType instanceof BigDecimal) {
matched = true;
}
}
assertThat("response data type cannot match the expectation.", matched, equalTo(true));
}
}
......@@ -15,4 +15,5 @@ public class BackendServiceProperties {
private String host;
private String port;
private String openApiDefinitionPath;
private String httpSchema;
}
package com.fuzamei.autotest.testdata;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@Component
@Configuration
@Slf4j
@Setter
@Getter
public class GetPreparedData {
}
server.port=8080
logging.level.org.springframework=ERROR
logging.level.com.fuzamei.autotest=DEBUG
backEndService.httpSchema="http://"
backEndService.host="172.22.18.152"
backEndService.port="2345"
backEndService.openApiDefinitionPath=""
testRel="2.1.0"
testRel="v2.1.0"
package com.fuzamei.autotest.steps.openApi;
package com.fuzamei.autotest.steps.openapi;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fuzamei.autotest.openapi.ParseOpenApi;
import com.fuzamei.autotest.properties.BackendServiceProperties;
import com.fuzamei.autotest.properties.GlobalProperties;
import io.cucumber.java.en.Given;
......@@ -8,7 +8,8 @@ import io.cucumber.java.en.Then;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.File;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
@Slf4j
public class ApiVerification {
......@@ -18,20 +19,16 @@ public class ApiVerification {
@Autowired
private BackendServiceProperties backendServiceProperties;
private String classPath = this.getClass().getClassLoader().getResource("/").getPath();
@Autowired
private ParseOpenApi parseOpenApi;
@Given("^Found OpenAPI definition for {string} service$")
@Given("^Found OpenAPI definition for (.*?) service$")
public void foundOpenApiProfileForTestingVer(String serviceName) throws Throwable {
String release = globalProperties.getTestRel();
String openApiFilePath = classPath + File.separator +
"test" + File.separator +
"resources" + File.separator +
"testingData" + File.separator +
globalProperties.getTestRel() + File.separator +
serviceName + "OpenAPI.json";
log.debug("{} openAPI definition path is: {}", serviceName, openApiFilePath);
switch (serviceName) {
case "backend":
String openApiFilePath = parseOpenApi.getOpenApiFilePathByVersion(serviceName, release);
assertThat(openApiFilePath, is(not(emptyString())));
backendServiceProperties.setOpenApiDefinitionPath(openApiFilePath);
break;
default:
......@@ -39,8 +36,8 @@ public class ApiVerification {
}
}
@Then("^Analyze OpenAPI and send requests to {string} service$")
public void sendReqToTargetService(String serviceName) throws Throwable {
@Then("^Analyze OpenAPI file of (.*?) service and generate REST-ful requests automatically$")
public void parseOpenApiAndSendReqToTargetService(String serviceName) throws Throwable {
String targetServiceApiFilePath = "";
switch (serviceName) {
case "backend":
......@@ -53,8 +50,9 @@ public class ApiVerification {
default:
break;
}
ObjectMapper objectMapper = new ObjectMapper();
assertThat(parseOpenApi.analyzeOpenApiAndGenerateRequests(serviceName, targetServiceApiFilePath), is(Boolean.TRUE));
}
}
package com.fuzamei.autotest.steps.openApi;
package com.fuzamei.autotest.steps.openapi;
public class MockService {
}
package com.fuzamei.autotest.steps.testdata;
public class HandleTestData {
//add-a-user: user-id, user-name
//https://www.cnblogs.com/Marydon20170307/p/13606372.html
}
@api_backend
Feature: API tests for service of backend
@part1
@verifyApi
Scenario: Verify the exposed APIs work as design
Given Found OpenAPI definition for backend service
Then Analyze OpenAPI and send requests to backend service
\ No newline at end of file
When Most basic testing data is ready for backend service
Then Analyze OpenAPI file of backend service and generate REST-ful requests automatically
Then Send requests to backend service and check response
\ No newline at end of file
u_userinfo:
id: 11000000
phone: 13999999999
name: automation
#password: admin
password: F6889AA527EA40FB0A2AECC5A28A694E
mail: xie.qin@33.cn
u_organization:
id: 49d8e2dcb4ed41b5b564ab1608322613
name: automation
address: automation
type: automation
creator: 11000000
owner: 11000000
status: 1
u_org_user:
id: 5678
user_id: 11000000
org_id: 49d8e2dcb4ed41b5b564ab1608322613
status: 0
u_federation:
id: 896bc3c9312a4759a5d5839be79cdda5
name: automation
creator: 11000000
owner: 11000000
{
"u_userinfo": {
"id": 87654321,
"phone": 13999999999,
"name": "automation",
"passwd": "admin",
"password": "F6889AA527EA40FB0A2AECC5A28A694E",
"mail": "xie.qin@33.cn"
},
"u_organization": {
"id": "49d8e2dcb4ed41b5b564ab1608322613",
"name": "automation",
"address": "automation",
"type": "automation",
"creator": 87654321,
"owner": 87654321,
"status": 1
},
"u_org_user": {
"id": 87654321,
"user_id": 87654321,
"org_id": "49d8e2dcb4ed41b5b564ab1608322613",
"status": 0
},
"u_federation": {
"id": "896bc3c9312a4759a5d5839be79cdda5",
"name": "automation",
"creator": 87654321,
"owner": 87654321
}
}
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