版權聲明:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
本文鏈接:https://blog.csdn.net/sinat_34766121/article/details/89084983GitHub:
https://github.com/reese0329/testng
接口測試用例編寫
Testng使用
斷言使用
接口測試框架Rest assured使用
Jsonschema的使用
接口測試主要關注點響應結構
數據來源
數據格式
數據正確性
業務邏輯
用例設計—參數要考慮的
用例設計—結果要驗證的
數據庫交互是否生效: 點贊,數據庫數據變化
用例的管理與維護
•功能測試模式:為了更快速會選用EXCEL、思維導圖進行用例管理。
•自動化測試模式:使用測試腳本進行管理。
注:
對於接口的提測,建議是分批提測,最好不要所有接口統一提測,分批測試可以在較短的時間內完成接口測試,也可以提前輔助客戶端進行聯調,不會占用較長的項目周期。
接口需求分析結合需求文檔+接口文檔來進行對照分析
•分析出需求文檔中所有涉及接口的功能點,並羅列功能點相關業務邏輯
•分析接口功能點是否包含在了接口文檔中
•分析接口文檔中描述的實現是否能夠滿足或者覆蓋接口功能點及其業務邏輯
•是否需要上層服務支持,服務端是否已提交數據需求
建議先與服務端達成一致:
•需求宣講完成后,優先產出接口文檔(便於測試同學進行接口分析)
•接口文檔與客戶端先行確認,再進行接口開發(預防后續反工的風險)
•服務端提供接口開發排期表(方便進行測試排期)
16'
<dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <version>6.1.1</version> <scope>test</scope> </dependency>
37'
在pom.xml中引入testng測試框架
將功能測試用例轉換為腳本
import org.testng.annotations.Test; public class testng { @Test public void abc(){ System.out.println("這里是abc"); } }
運行多個測試用例
import org.testng.annotations.Test; public class testng { @Test public void abc(){ System.out.println("這里是abc"); } @Test public void abc1(){ System.out.println("這里是abc1"); } @Test public void abc2(){ System.out.println("這里是abc2"); } }
四、注解說明
TestNG支持多種注解,可以進行各種組合,如下進行簡單的說明
注解 描述
@BeforeSuite 在該套件的所有測試都運行在注釋的方法之前,僅運行一次
@AfterSuite 在該套件的所有測試都運行在注釋方法之后,僅運行一次
@BeforeClass 在調用當前類的第一個測試方法之前運行,注釋方法僅運行一次
@AfterClass 在調用當前類的第一個測試方法之后運行,注釋方法僅運行一次
@BeforeTest 注釋的方法將在屬於test標簽內的類的所有測試方法運行之前運行
@AfterTest 注釋的方法將在屬於test標簽內的類的所有測試方法運行之后運行
@BeforeGroups 配置方法將在之前運行組列表。 此方法保證在調用屬於這些組中的任何一個的第一個測試方法之前不久運行
@AfterGroups 此配置方法將在之后運行組列表。該方法保證在調用屬於任何這些組的最后一個測試方法之后不久運行
@BeforeMethod 注釋方法將在每個測試方法之前運行
@AfterMethod 注釋方法將在每個測試方法之后運行
@DataProvider 標記一種方法來提供測試方法的數據。 注釋方法必須返回一個Object [] [],其中每個Object []可以被分配給測試方法的參數列表。 要從該DataProvider接收數據的@Test方法需要使用與此注釋名稱相等的dataProvider名稱
@Factory 將一個方法標記為工廠,返回TestNG將被用作測試類的對象。 該方法必須返回Object []
@Listeners 定義測試類上的偵聽器
@Parameters 描述如何將參數傳遞給@Test方法
@Test 將類或方法標記為測試的一部分,此標記若放在類上,則該類所有公共方法都將被作為測試方法
常用注解使用:
1.@BeforeClass @AfterClass 在運行類之前或之后執行一次
@BeforeClass 中可寫入初始數據
@AfterClass 清除數據
testng_before_after_class.java
import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; public class testng_before_after_class { // 運行testng_before_after_class類的時,會執行一次 @BeforeClass public void BeforeClass(){ System.out.println("BeforeClass被運行"); } @AfterClass public void AfterClass(){ System.out.println("AfterClass被運行"); } // 運行每個測試方法進都會被執行到 @Test public void abc(){ System.out.println("這里是abc"); } @Test public void abc1(){ System.out.println("這里是abc1"); } @Test public void abc2(){ System.out.println("這里是abc2"); } }
創建xml文件
class name需要與測試的class類名一致
一個測試套件可以包含多個類
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <!--可以同時開啟多條線程進行測試任務--> <suite name="Suite1" parallel="methods" thread-count="2"> <test name="test12"> <classes> <class name="testng_before_after_class" > </class> </classes> </test> </suite>
2.將測試結果輸出到一個xml文件中
https://www.jianshu.com/p/5b5ab46f6ec0
Run -> Edit Configurations
3.method
@BeforeMethod
public void BeforeMethod(){
System.out.println("BeforeMethod被運行");
}
@AfterMethod
public void AfterMethod(){
System.out.println("AfterMethod被運行");
}
import org.testng.annotations.*;
public class testng_method {
// 運行每個測試方法進都會被執行到
@BeforeMethod
public void BeforeMethod(){
System.out.println("BeforeMethod被運行");
}
@AfterMethod
public void AfterMethod(){
System.out.println("AfterMethod被運行");
}
@Test
public void abc(){
System.out.println("這里是abc");
}
@Test
public void abc1(){
System.out.println("這里是abc1");
}
@Test
public void abc2(){
System.out.println("這里是abc2");
}
}
4. @Test(enabled = false)
// 未完成的用例,不希望被執行 @Test(enabled = false) public void function3(){ System.out.println(33); }
import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; public class testng_enable { @Test(enabled = false) public void abc(){ System.out.println("這里是abc"); } @Test public void abc1(){ System.out.println("這里是abc1"); } @Test public void abc2(){ System.out.println("這里是abc2"); } }
5.執行順序
a.testng_order.xml 按xml中方法順序執行
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <!--可以同時開啟多條線程進行測試任務--> <suite name="Suite1" parallel="methods" thread-count="2"> <test name="test12"> <classes> <class name="testng_enable" > <methods> <include name="abc2" /> <include name="abc1" /> </methods> <!--<class name="testng_base" >--> </class> </classes> </test> </suite>
b.按默認順序執行 <test name="test12" preserve-order="true">
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <!--可以同時開啟多條線程進行測試任務--> <suite name="Suite1" parallel="methods" thread-count="2"> <test name="test12" preserve-order="true"> <classes> <class name="testng_enable" > <methods> <include name="abc2" /> <include name="abc1" /> </methods> <!--<class name="testng_base" >--> </class> </classes> </test> </suite>
6.分組測試
//將多個測試用例進行分組 @Test(groups = {"group1"}) public void function4(){ System.out.println(444); } @Test(groups = {"group1"}) public void function5(){ System.out.println(555); } @Test(groups = {"group2"}) public void function6(){ System.out.println(666); }
testng_group
import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; public class testng_group { @Test(groups = "group1") public void abc(){ System.out.println("這里是abc"); } @Test(groups = "group1") public void abc1(){ System.out.println("這里是abc1"); } @Test(groups = "group2") public void abc2(){ System.out.println("這里是abc2"); } }
testng_group.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <!--可以同時開啟多條線程進行測試任務--> <suite name="Suite1" parallel="methods" thread-count="2"> <test name="test_group" > <groups> <run> <include name="group1" /> </run> </groups> <classes> <class name="testng_group" > <!--<class name="testng_base" >--> </class> </classes> </test> </suite>
不在分組的里的測試用例不會被執行
7.依賴測試
登錄信息
依賴兩個函數
import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; public class testng_depend { @Test public void abc(){ System.out.println("這里是abc"); } @Test public void abc1(){ System.out.println("這里是abc1"); } @Test public void abc2(){ System.out.println("這里是abc2"); } @Test(dependsOnMethods = {"abc","abc2"}) public void function(){ System.out.println(444); } }
run function函數
run testng_depend類
1‘8
8.構造參數化
testng_parameters.java
import org.testng.annotations.BeforeTest; import org.testng.annotations.Parameters; import org.testng.annotations.Test; public class testng_parameters { //參數化 @Test @Parameters("username") public void function8(String test1) { System.out.println("name == " + test1); } }
testng_parameters.xml
<parameter name="username" value="trx" />
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <!--可以同時開啟多條線程進行測試任務--> <suite name="Suite_parameters" parallel="methods" thread-count="2"> <test name="test0411"> <parameter name="username" value="trx" /> <classes> <class name="testng_parameters" > </class> </classes> </test> </suite>
junit開發用來做單元測試
1‘15
9.線程
提高測試效率 並行執行
類並行執行 parallel="classes"
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <!--可以同時開啟多條線程進行測試任務--> <suite name="Suite1" parallel="classes" thread-count="2"> <test name="test12"> <classes> <class name="testng_enable" > </class> <class name="testng_method" > </class> </classes> </test> </suite>
方法並行執行 parallel="methods"
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <!--可以同時開啟多條線程進行測試任務--> <suite name="Suite1" parallel="methods" thread-count="2"> <test name="test12"> <classes> <class name="testng_enable" > </class> <!--<class name="testng_method" >--> <!--</class>--> </classes> </test> </suite>
斷言assertDemo
引入testng斷言
import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Parameters; import org.testng.annotations.Test; public class testng_assert { @Test public void assertTest(){ String str1 = "testerhome"; String str2 = "testerhome"; Assert.assertEquals(str1,str2,"判斷是否相等"); boolean bl = true; boolean b2 = false; Assert.assertTrue(bl,"判斷是否為true"); Assert.assertFalse(b2,"判斷是否為false"); Object str3 = null ; Assert.assertNull(str3,"判斷是否為nul"); Assert.assertNotNull(str3,"判斷是否不為null"); } }
import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Parameters; import org.testng.annotations.Test; public class testng_assert { @Test public void assertTest(){ String str1 = "testerhome"; String str2 = "testerhome1"; Assert.assertEquals(str1,str2,"判斷是否相等"); boolean bl = true; boolean b2 = false; Assert.assertTrue(bl,"判斷是否為true"); Assert.assertFalse(b2,"判斷是否為false"); Object str3 = null ; Assert.assertNull(str3,"判斷是否為nul"); Assert.assertNotNull(str3,"判斷是否不為null"); } }
1‘19
接口測試框架的選型推薦使用Rest assured框架進行接口測試,語法足夠簡單,編寫測試用例速度快,而且還提供了
相應的斷言機制、json驗證以及封裝了相關jsonpath、xmlpath,使接口測試更加方便快捷。
rest_assured.java
<dependency> <groupId>io.rest-assured</groupId> <artifactId>rest-assured</artifactId> <version>3.0.2</version> </dependency> <dependency> <groupId>io.rest-assured</groupId> <artifactId>json-path</artifactId> <version>3.0.2</version> </dependency> <dependency> <groupId>io.rest-assured</groupId> <artifactId>json-schema-validator</artifactId> <version>3.0.2</version> </dependency>
發起請求 get("https://testerhome.com/api/v3/topics.json?limit=1&offset=0&type=last_actived").prettyPeek();
.prettyPeek() 打印返回結果
import io.restassured.response.Response; import org.testng.Assert; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.List; import static io.restassured.RestAssured.get; import static io.restassured.RestAssured.given; import static io.restassured.module.jsv.JsonSchemaValidator.matchesJsonSchemaInClasspath; public class rest_assured { @Test public void getfunction1() throws Exception { //結構驗證 get("https://testerhome.com/api/v3/topics.json?limit=1&offset=0&type=last_actived") .prettyPeek(); } }
入參
import io.restassured.response.Response; import org.testng.Assert; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.List; import static io.restassured.RestAssured.get; import static io.restassured.RestAssured.given; import static io.restassured.module.jsv.JsonSchemaValidator.matchesJsonSchemaInClasspath; public class rest_assured { @Test public void getfunction2() throws Exception { // int statuscode = given().param("limit", 2).and().param("offset", 0).and() .param("type", "last_actived") .get("https://testerhome.com/api/v3/topics.json").prettyPeek(); } }
返回信息同上
given().param("valueName","value").and.param("valueName1","value1").get("url")
import io.restassured.response.Response; import org.testng.Assert; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.List; import static io.restassured.RestAssured.get; import static io.restassured.RestAssured.given; import static io.restassured.module.jsv.JsonSchemaValidator.matchesJsonSchemaInClasspath; public class rest_assured { @Test public void getfunction2() throws Exception { Response response = given().param("limit", 2).and().param("offset", 0).and() .param("type", "last_actived") .get("https://testerhome.com/api/v3/topics.json"); // response.getCookies(); // response.getContentType(); Assert.assertEquals(200,response.statusCode()); } }
將請求數據放Map,將Map賦值給URL
@Test public void getfunction3() throws Exception { Map<String, Object> parameters = new HashMap<String, Object>(); parameters.put("limit", 2); parameters.put("offset", 0); parameters.put("type", "last_actived"); int statuscode = given().params(parameters).get("https://testerhome.com/api/v3/topics.json") .getStatusCode(); }
post請求
@Test public void postfunction2() throws Exception { Response response = given() .cookie("ll=\"108288\"; bXXXXXXXXXXXXXXXXXXXX") .contentType("application/x-www-form-urlencoded") .param("ck","aIY0") .param("comment","test0411") .post("https://www.douban.com/"); // response.getCookies(); // response.getContentType(); Assert.assertEquals(200,response.statusCode()); }
入參
@Test public void postfunction3() throws Exception { Map<String, Object> parameters = new HashMap<String, Object>(); parameters.put("ck","aIY0"); parameters.put("comment","test0411"); int statuscode = given() .cookie("ll=\"108288\"; bid=xxxxxx") .contentType("application/x-www-form-urlencoded") .params(parameters) .post("https://www.douban.com/").getStatusCode(); }
支持put請求
1‘31
斷言取值獲取返回信息
@Test public void assert_Test() throws Exception { //斷言驗證 Response response = get("https://testerhome.com/api/v3/topics.json?limit=1&offset=0&type=last_actived"); int statuscode = response.statusCode(); Assert.assertEquals(200, statuscode, "接口返回正確"); String title = response.jsonPath().getString("topics[0].title"); Assert.assertEquals("QA 最佳實踐:大廠如何提升軟件質量?|福利",title,"標題正確"); }
獲取topic數量並斷言有3個topic
@Test public void assert_Test() throws Exception { //斷言驗證 Response response = get("https://testerhome.com/api/v3/topics.json?limit=1&offset=0&type=last_actived"); int statuscode = response.statusCode(); Assert.assertEquals(200, statuscode, "接口返回正確"); String title = response.jsonPath().getString("topics[0].title"); Assert.assertEquals("QA 最佳實踐:大廠如何提升軟件質量?|福利",title,"標題正確"); //獲取topic數量 int topicsize = response.jsonPath().getList("topics").size(); Assert.assertEquals(3,topicsize,"topic sum = 3"); }
@Test public void assert_Test_topicsize() throws Exception { //斷言驗證 Response response = get("https://testerhome.com/api/v3/topics.json?limit=1&offset=0&type=last_actived"); //獲取topic數量 int topicsize = response.jsonPath().getList("topics").size(); List list = new ArrayList(); list.add("QA 最佳實踐:大廠如何提升軟件質量?|福利"); for (int i=0;i<topicsize;i++) { Assert.assertEquals( list.get(0), response.jsonPath().getString("topics["+i+"].title")); } }
比對所有結果
1‘38
結果有問題
@Test public void assert_Test_topicsize() throws Exception { //斷言驗證 Response response = get("https://testerhome.com/api/v3/topics.json?limit=1&offset=0&type=last_actived"); //獲取topic數量 int topicsize = response.jsonPath().getList("topics").size(); //提前構造 List list = new ArrayList(); list.add("QA 最佳實踐:大廠如何提升軟件質量?|福利"); list.add("接口測試線下 Workshop 上海站"); list.add("TesterHome 廣州沙龍 2019年 第 1 期報名中!"); for (int i=0;i<topicsize;i++) { Assert.assertEquals( list.get(i), response.jsonPath().getString("topics[" + i + "].title")); } }
1‘’43‘43
數據加工 前端只顯示3個字符
數據比對
1‘45
Jsonschema的使用http://json-schema.org
Json schema的編寫
在rest assured中的應用
http://json-schema.org/draft-04/schema#json-schema版本為draft-04
優先驗證接口結構
驗證返回的json結構是否有缺失
邏輯
數據的准確性 -- 寫循環驗證所有數據的准確性
接口的類型也很重要!!!
@Test public void jsonscheme(){ get("https://testerhome.com/api/v3/topics.json?limit=1&offset=0&type=last_actived") .then().assertThat().body(matchesJsonSchemaInClasspath("topics.json")); }
新建示例
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "topics",
"type": "object",
"properties": {
"topics": {
"type": "array",
"items":{
"type":"object",
"properties":{
"id":{ "type":"integer"},
"title":{"type":"string"},
"user":{"type": "object",
"properties": {
"id":{"type": "integer"},
"login":{"type": "string"}
}},
"deleted":{"type":"boolean"},
"closed_at":{"type":"null"}
},
"required":["id","title","created_at"]
}
}
},
"required": [
"topics"
]
}
缺失驗證
驗證Jsonschema編寫正確
http://json-schema-validator.herokuapp.com/
參考 https://blog.csdn.net/df0128/article/details/83243822