12 月 23 号去上海参加了论坛举办的活动,干货蛮多的,感觉别人家做的都好腻害,
其中 Lego 接口自动化测试印象挺深的,通过配置文件的方式生成接口测试用例,
因为恰好我们公司也准备做接口自动化的部分,之前还没有这部分
这是我在参加活动前的方案,有同事推荐 retrofit 不错,自己写了一下,感觉不方便
在论坛上看到的 rest-assured 挺不错的,用起来方便的
@Slf4j
@Listeners({InterfaceFailureHandle.class, Retry.class})
public abstract class BaseInterfaceTest {
public static Map<String, String> testParametersMap = new HashMap<>();
static {
/** 公共参数 **/
testParametersMap.put("appKey", "android_111");
testParametersMap.put("appTimestamp", String.valueOf(System.currentTimeMillis()));
testParametersMap.put("appTypeId", "0");
testParametersMap.put("cookieId", "eee");
testParametersMap.put("countryCode", "SA");
testParametersMap.put("currency", "SAR");
}
@BeforeClass
@Parameters({"baseUrl", "appVersion"})
public void init(@Optional String baseUrl, @Optional String appVersion) {
log.debug("init");
if (baseUrl == null) {
baseUrl= "http://weekly.test.com";
}
if (appVersion != null) {
testParametersMap.put("appVersion", appVersion);
}
RestAssured.baseURI = baseUrl;
RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
RestAssured.requestSpecification = new RequestSpecBuilder().build().accept(JSON).contentType(JSON);
}
}
@Slf4j
@Listeners({InterfaceFailureHandle.class})
public class LoginNew extends BaseInterfaceTest{
@Test
public void loginWithUerNamePassword() {
log.debug("actions");
JSONObject jsonObject = new JSONObject();
testParametersMap.put("userName", "carl@163.com");
testParametersMap.put("password", "kkkkkk");
jsonObject.putAll(testParametersMap);
testParametersMap.put("sign", SignGen.getSign(jsonObject, SignGen.appSecret));
jsonObject.putAll(testParametersMap);
log.debug(jsonObject.toJSONString());
given().body(jsonObject.toJSONString())
.when().post("/user/login").then()
.body("messageCode", is("0"),
"messageType", is(0));
}
}
简单学习了一下 yaml 配置文件的写法,还是没完全搞清楚,
所以我选择写出 javabean 出来先,然后 dump 出来,再按 dump 出来的格式学着具体写
@Data
public class APITestProject {
private String baseUrl;
private Map<String, Object> globalRequestParmeters = new HashMap<>();
private Map<String, APITestSuite> testSuites = new HashMap<>();
}
@Data
public class APITestSuite {
private String description;
private List<APITestCase> testCaseList = new ArrayList<>();
private Map<String, Object> testSuiteParameters = new HashMap<>();
}
@Data
public class APITestCase {
private String name;
private String description;
private String apiUrl;
private Map<String, String> sqlCommands = new HashMap<>();
private Map<String, Object> requestParameters = new HashMap<>();
private Map<String, Map<String, Object>> resultVerify = new HashMap<>();
}
一个 project 下面有多个 testsuite,一个 testsuite 下面有多个用例,然后全局参数,测试套参数,用例参数,校验结果
!!APITestProject
baseUrl: http://weekly.test.com
#全局参数
globalRequestParmeters:
appVersion: '6.12'
appTypeId: '0'
countryCode: SA
cookieId: e0dc298b-2e2d-4f14-ab80-cfcce4471679
appKey: android_lk98f83
currency: SAR
lang: '0'
appTimestamp: '1514204071822'
terminalType: '1'
#测试套集合
testSuites:
firstSuite:
description: testSuiteDesc
#测试用例集合
testCaseList:
- apiUrl: /user/login
description: kkk
name: loginNew
requestParameters:
password: kkkkkk
userName: clark@163.com
resultVerify:
#结果校验
is:
messageType: 0
messageCode: '0'
#sql,在测试前或测试后做数据准备或还原,未完成
sqlCommands: {}
#测试套参数集合
testSuiteParameters: {}
@Slf4j
@Listeners({InterfaceFailureHandle.class, Retry.class})
public class APITestExecutor {
private static Map<String, Object> testParametersMap = new HashMap<>();
private static APITestProject apiTestProject;
static {
Yaml yaml = new Yaml();
try {
apiTestProject = yaml.loadAs(new FileInputStream(new File("src\\main\\resources\\InterfaceTest.yaml")), APITestProject.class);
testParametersMap.putAll(apiTestProject.getGlobalRequestParmeters());
} catch (FileNotFoundException e) {
e.printStackTrace();
log.debug(e.getMessage());
}
}
@BeforeMethod
@Parameters({"baseUrl", "appVersion"})
public void init(@Optional String baseUrl, @Optional String appVersion) {
log.debug("init");
if (baseUrl == null) {
baseUrl = apiTestProject.getBaseUrl();
}
if (appVersion != null) {
testParametersMap.put("appVersion", appVersion);
}
RestAssured.baseURI = baseUrl;
RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
RestAssured.requestSpecification = new RequestSpecBuilder().build().accept(JSON).contentType(JSON);
}
@Test
public void executor() {
JSONObject jsonObject = new JSONObject();
Map<String, APITestSuite> testSuites = apiTestProject.getTestSuites();
for (Map.Entry<String, APITestSuite> entry : testSuites.entrySet()) {
List<APITestCase> testCases = entry.getValue().getTestCaseList();
for (APITestCase testCase : testCases) {
testParametersMap.putAll(testCase.getRequestParameters());
jsonObject.putAll(testParametersMap);
testParametersMap.put("sign", SignGen.getSign(jsonObject, SignGen.appSecret));
jsonObject.putAll(testParametersMap);
log.debug(jsonObject.toJSONString());
ValidatableResponse validatable = given().body(jsonObject.toJSONString()).when().post(testCase.getApiUrl()).then();
Map<String, Map<String, Object>> resultVerify = testCase.getResultVerify();
resultVerify.forEach((condition, v) -> {
v.forEach((key, value) -> {
try {
Matchers matches = new Matchers();
Matcher matcher = (Matcher) Matchers.class.getDeclaredMethod(condition, Object.class).invoke(matches, value);
validatable.body(key, matcher);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
});
});
validatable.log().all();
}
}
}
}
读取 yaml 文件并遍历,执行测试
代码:
配置文件:
通过代码来运行 testNG 的测试用例,读取用例后,放入队列中,每次执行时读取一条用例即可
public class APITestRun {
public static void main(String[] args) {
for (int i=0; i<APITestUtils.getBlockingQueue().size(); i++) {
TestNG testNG = new TestNG();
testNG.setTestClasses(new Class[]{APITestExecutor.class});
testNG.run();
}
}
}
@Slf4j
@Listeners({InterfaceFailureHandle.class, Retry.class})
public class APITestExecutor {
private APITestCase testCase;
@BeforeMethod
@Parameters({"baseURL"})
public void init(@Optional String baseURL) {
log.debug("init");
if (baseURL == null) {
baseURL = APITestUtils.getApiTestProject().getBaseURL();
}
RestAssured.baseURI = baseURL;
RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
RestAssured.requestSpecification = new RequestSpecBuilder().build().accept(JSON).contentType(JSON);
testCase = APITestUtils.getTestCase();
}
@Test
public void executor() {
JSONObject jsonObject = new JSONObject();
jsonObject.putAll(APITestUtils.getApiTestProject().getGlobalRequestParmeters());
jsonObject.putAll(testCase.getRequestParameters());
String sign = SignGenerate.getSign(jsonObject, SignGenerate.appSecret);
jsonObject.put("sign", sign);
log.debug(jsonObject.toJSONString());
ValidatableResponse validatableResponse = given().body(jsonObject.toJSONString()).when().post(testCase.getApiUrl()).then();
Map<String, Map<String, Object>> resultVerify = testCase.getResponseVerify();
resultVerify.forEach((condition, v) -> {
v.forEach((key, value) -> {
try {
Matchers matchers = new Matchers();
Matcher matcher = (Matcher) Matchers.class.getDeclaredMethod(condition, Object.class).invoke(matchers, value);
validatableResponse.body(key, matcher);
} catch (Exception e) {
e.printStackTrace();
}
});
});
validatableResponse.log().all();
updateTestCaseInfo();
}
/**
* 更新测试用例的名字和描述信息
*/
public void updateTestCaseInfo() {
TestCaseEvent testCaseEvent = new TestCaseEvent() {
@Override
public void process(TestCaseResult testCaseResult) {
log.debug("name:" + APITestUtils.getCurrentTestCase().getName());
log.debug("description:" + APITestUtils.getCurrentTestCase().getDescription());
testCaseResult.setName(APITestUtils.getCurrentTestCase().getName());
ru.yandex.qatools.allure.model.Description description = new ru.yandex.qatools.allure.model.Description();
description.setValue(APITestUtils.getCurrentTestCase().getDescription());
testCaseResult.setDescription(description);
// testCaseResult.getLabels().add(new Label().withName("testSuite").withValue(""));
}
};
APITestUtils.getAllure().fire(testCaseEvent);
}
}
@Slf4j
public final class APITestUtils {
private static APITestProject apiTestProject;
private static BlockingQueue<APITestCase> blockingQueue = new LinkedBlockingQueue<>();
private static APITestCase currentTestCase;
private static Allure allure = Allure.LIFECYCLE;
private APITestUtils() {
}
static {
Yaml yaml = new Yaml();
try {
// 加载用例文件
apiTestProject = yaml.loadAs(new FileInputStream(new File("src\\main\\resources\\InterfaceTest.yaml")), APITestProject.class);
} catch (FileNotFoundException e) {
e.printStackTrace();
log.debug(e.getMessage());
}
/** 遍历用例并放入队例中 **/
Map<String, APITestSuite> testSuites = apiTestProject.getTestSuites();
for (Map.Entry<String, APITestSuite> entry : testSuites.entrySet()) {
List<APITestCase> testCases = entry.getValue().getTestCaseList();
blockingQueue.addAll(testCases);
}
}
public static BlockingQueue<APITestCase> getBlockingQueue() {
return blockingQueue;
}
public static APITestCase getTestCase() {
currentTestCase = blockingQueue.poll();
return currentTestCase;
}
public static APITestCase getCurrentTestCase() {
return currentTestCase;
}
public static APITestProject getApiTestProject() {
return apiTestProject;
}
public static Allure getAllure() {
return allure;
}
TestNG 测试注解以及生命周期:
@BeforeClass(执行一次)
@BeforeMethod(N 个 Test 方法执行 N 次)
@Test Test 方法(此注解可能在类上表示多个,在方法表示一个)
@AfterMethod(N 个 Test 方法执行 N 次)
@AfterClass(执行一次)
LinkedBlockingQueue
poll: 若队列为空,返回 null。
remove:若队列为空,抛出 NoSuchElementException 异常。
take:若队列为空,发生阻塞,等待有元素。
okhttp EOF Exception
见=>https://blog.csdn.net/m_xiaoer/article/details/72858895
看别人做的好漂亮,好厉害,自己动手尝试去做的时候,就感觉坑也不少的