前言
接口自動化測試棚饵,是所有測試人員必備的一項技能骗灶,建立一套穩(wěn)定混驰、可用率高的測試用例集,能夠幫助測試人員提升測試效率闲昭,是日常工作中不可或缺的小技能。本文主要基于業(yè)界使用廣泛的TestNG框架進行展開靡挥,介紹TestNG在實際工程實踐中的應用序矩。
眾所周知,TestNG之所以能夠作為一款優(yōu)秀的測試框架被廣泛應用跋破,主要在于其具備Annotation簸淀、依賴性測試、支持并發(fā)測試毒返、支持錯誤重試啃擦、用例管理、以及測試報告的自定義等特性饿悬,能夠幫助測試人員對測試用例進行高效的管理和維護令蛉,看看如何基于TestNG框架解決測試過程中的一些痛點。
痛點
- 自動化case多,用例執(zhí)行順序混亂珠叔。
在測試過程中蝎宇,常常會遇到場景A依賴于場景B的情況,舉個栗子:
當我創(chuàng)建了一個typeA的廣告祷安,針對廣告A我需要創(chuàng)建一些創(chuàng)意姥芥,關于typeA廣告的case執(zhí)行完成后,需要刪除typeA廣告汇鞭。再創(chuàng)建typeB的廣告凉唐,再執(zhí)行typeB廣告的case。但是在使用testng過程中發(fā)現(xiàn)霍骄,在同一個測試類中台囱,@test默認測試方法的執(zhí)行順序是按照方法名的首字母升序排序執(zhí)行的,這就會導致在執(zhí)行typeA廣告相關case執(zhí)行了一半后读整,又去執(zhí)行typeB廣告相關的case簿训。而我們針對case執(zhí)行的前置條件,是以group維度進行配置的米间,那么就會出現(xiàn)在執(zhí)行typeA相關的case時强品,typeA的廣告已經(jīng)被刪除了。 - 自動化不穩(wěn)定
在自動化用例的實際應用時屈糊,經(jīng)常會出現(xiàn)的榛,單獨跑一個case的時候,是成功的逻锐,而跑全量case時夫晌,經(jīng)常會出現(xiàn)部分case失敗的情況。 - 異步接口多谦去,等待耗時過長
在一些高并發(fā)場景中慷丽,服務器需要處理的事務較多蹦哼,很多接口采用的是異步調(diào)用的方式鳄哭,等待時間不確定,常常導致接口的返回結(jié)果和預期不符纲熏,斷言失敗妆丘。
那么接下來主要針對以上幾點,介紹一下以上問題的解決方案局劲。
痛點解決方案及優(yōu)化
- 自動化case多勺拣,用例執(zhí)行順序混亂
針對這種現(xiàn)象,TestNg其實提供了很好的解決方案:
- Method1.
在testclass維度鱼填,可以通過testng.xml來進行管理
<test name="創(chuàng)建feeds卡片" preserve-order="true">
<classes>
<class name="bce.feed.IFeedCardType1"/>
<class name="bce.feed.IFeedCardType2"/>
<class name="bce.feed.IFeedCardType3"/>
<class name="bce.feed.IFeedCardType7"/>
</classes>
</test>
如上药有,通過presever-order這個屬性,來控制case按順序執(zhí)行。
- Method2.
testclass中的每一個case的執(zhí)行順序是不受preserve-order控制的愤惰,而是按case首字母的順序來執(zhí)行苇经。這時就需要通過priority來指定執(zhí)行順序(默認值為0),數(shù)值越小宦言,越靠前執(zhí)行扇单。
step1.創(chuàng)建監(jiān)聽器
public class RePrioritizingListener implements IAnnotationTransformer {
HashMap<Object, Integer> priorityMap = new HashMap<Object, Integer>();
Integer class_priorityCounter = 10000;
Integer max_testpriorityLength = 4;
@Override
public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
Class<?> declaringClass = testMethod.getDeclaringClass();
Integer test_priority = annotation.getPriority();
Integer current_ClassPriority = priorityMap.get(declaringClass);
//修改當前類中的@test的priority值
if (current_ClassPriority == null) {
current_ClassPriority = class_priorityCounter++;
priorityMap.put(declaringClass, current_ClassPriority);
}
String concatenatedPriority = test_priority.toString();
while (concatenatedPriority.length() < max_testpriorityLength) {
concatenatedPriority = "0" + concatenatedPriority;
}
concatenatedPriority = current_ClassPriority.toString() + concatenatedPriority;
annotation.setPriority(Integer.parseInt(concatenatedPriority));
}
}
加這個監(jiān)聽的原因是:priority是在testng開始時候一次性加載進去的,不同類中priority相同的case會一起執(zhí)行奠旺,仍然會出現(xiàn)case亂序的情況蜘澜。所以需要 implement IAnnotationTransformer 對每個class 的method priority 進行重置。
step2. 在testNG的xml中添加監(jiān)聽
<listener class-name="base.RePrioritizingListener"/>
step3.在測試類中添加priority值(默認值為0响疚,數(shù)值越小鄙信,越靠前執(zhí)行),并設置dependsOnGroups稽寒。
public class TestClassA {
@Test(priority=1,group = "1")
public testMethod1 (){
}
@Test(priority=2,group = "1")
public testMethod2 (){
}
@Test(priority=3,,group = "1")
public testMethod3 (){
}
}
public class TestClassB {
@Test(priority=1,dependsOnGroups= {"1"})
public void ttestMethod4() {
}
@Test(priority=2,dependsOnGroups= {"1"})
public void testMethod5() {
}
@Test(priority=3,dependsOnGroups= {"1"})
public void testMethod6() {
}
}
- 自動化不穩(wěn)定
自動化不穩(wěn)定的原因有很多扮碧,在case并發(fā)執(zhí)行的過程中,很可能由于環(huán)境的原因杏糙,會造成我們的用例失敗慎王。testNg的IRetryAnalyzer接口可以有效地幫助我們解決此類問題。具體實施步驟如下:
step1.添加監(jiān)聽宏侍,實現(xiàn)IAnnotationTransformer赖淤,重寫transform方法。
public class RetryListener implements IAnnotationTransformer {
@Override
public void transform(ITestAnnotation iTestAnnotation, Class testClass, Constructor constructor, Method method) {
IRetryAnalyzer iRetryAnalyzer= iTestAnnotation.getRetryAnalyzer();
if(iRetryAnalyzer==null){
iTestAnnotation.setRetryAnalyzer(CustomRetry.class);
}
}
}
這里做的工作就是掃描所有的@Test方法是否具有retryAnalyzer屬性谅河,沒有則為其設置重試屬性(即@Test(retryAnalyzer = CustomRetry.class))咱旱。而具體的重試機制,可以通過實現(xiàn)IRetryAnalyzer接口绷耍,進行自定義實現(xiàn)吐限。
step2.實現(xiàn)IRetryAnalyzer接口
public class CustomRetry implements IRetryAnalyzer {
private static final Logger logger = LoggerFactory.getLogger(CustomRetry.class);
private int initRetryCount = 0;
private int maxRetryCount = 3; //失敗重跑次數(shù)
@Override
public boolean retry(ITestResult iTestResult) {
if(initRetryCount < maxRetryCount){
initRetryCount++;
logger.debug("================ {}.{} 重試第{}次==============",iTestResult.getTestClass().getName(),iTestResult.getName(),initRetryCount);
try {
Thread.sleep(2000);
}catch (InterruptedException e) {
e.printStackTrace();
}
return true;
}
return false;
}
}
這邊我設置了失敗重跑次數(shù)為3,如果重跑失敗褂始,則會被ignore诸典。
step3. 應用
1.全局生效
在testng.xml中配置監(jiān)聽
<listeners>
<listener class-name="base.RetryListener"/>
</listeners>
2.單用例
@Test(groups = "filterCpm",retryAnalyzer = CustomRetry.class)
public void test_non_blueVip_not_filtered_cpm(){
boolean is_ad = blueVipBaseTest("123456");
Assert.assertTrue(is_ad);
}
效果如下圖,用例失敗后會重試三次
- 異步接口多崎苗,等待耗時過長
解決此類問題的方法是輪詢狐粱。
public void refreshRtbAdsData(Integer creativeId) throws InterruptedException{
int count = 10;//輪詢限制重試次數(shù)
HttpRequestUtil httpRequestUtil = new HttpRequestUtil();
httpRequestUtil.addheader("Content-Type", "application/json");
String refreshData = httpRequestUtil.getResponseBody(Config.rtbRefrshUrl,"get");
while (!refreshData.contains(String.valueOf(creativeId)) && count > 0){
httpRequestUtil.addheader("Content-Type", "application/json");
refreshData = httpRequestUtil.getResponseBody(Config.rtbRefrshUrl,"get");
count--;
Thread.sleep(2000);//控制請求頻率
}
}
上面的方法就是輪詢10次,每2000ms輪詢一次胆数,直到拿到所需的創(chuàng)意肌蜻。通過這種方式,我們用例集執(zhí)行時間減少了一半必尼,同時穩(wěn)定性也得以提升蒋搜。
4.自定義測試報告
自定義測試報告主要是方便對測試結(jié)果進行整理,同時提升定位問題的效率。具體步驟如下:
step1. 創(chuàng)建監(jiān)聽重寫onFinish方法豆挽。主要目的是對測試的結(jié)果進行整理打包酸休,以郵件的方式發(fā)送給相關責任人。
public class TestngListener extends TestListenerAdapter {
public void onFinish(ISuite iSuite) {
ReportStaticFactory.copyReportStatic();
ReplaceHtmlReportStaticLocation.replaceStatic();
try {
ZipUtil.doZip("test-output", "test-output/report.zip");
} catch (IOException var11) {
var11.printStackTrace();
}
String projectName = ISuiteInfo.getSuiteName(iSuite);
ArrayList<HashMap<String, Integer>> suiteResult = ISuiteInfo.getSuiteResult(iSuite);
Boolean suiteState = ISuiteInfo.getSuiteState(iSuite);
String testResultState = suiteState ? "Pass" : "Fail";
(new StringBuilder()).append(projectName).append(" - 測試結(jié)果 - ").append(testResultState).toString();
String htmlMailContent = null;
ArrayList failResults = ISuiteInfo.getFailResults(iSuite);
try {
HtmlMailContent.getHtmlMailContent(projectName, suiteState, suiteResult, failResults);
} catch (TemplateException | IOException var10) {
var10.printStackTrace();
}
}
}
step2.配置
<listeners>
<listener class-name="base.TestngListener"/>
</listeners>
總結(jié)
以上就是基于TestNG的一些自動化實踐的總結(jié)祷杈,后續(xù)如有更好的應用斑司,再做補充啦~