单元测试卷模板(单元测试标准卷可打印版)

单元测试卷模板(单元测试标准卷可打印版)

首页技巧更新时间:2024-08-23 21:04:10

昨天有个小伙伴问我,有没有什么现成的测试报告模板,由于昨天实在比较忙就没顾上,所以今个有时间赶紧补上。一般力所能及的事,只要我有时间都会为大家解决,但毕竟能力有限做不到的地方小伙伴们也多理解。

在这里插入图片描述

平时我们开发接口时,Junit 单元测试是最为常用的一种开发测试手段,很多时候测试其实只看接口是否正常返回结果就 ok 了。但有时间我们要测试一些特殊场景,如:接口超时测试等,就没什么太好的办法了,而 TestNG 实现容易的多。它与 JUnit 用法十分相似,只要你用过 JUnit 分分钟上手。

大致讲一下 TestNG 的几个重要概念,@Test 注解标注的方法是最小的执行单元,我们可以将这些单个的测试用例划分成 group 分组管理,group 可以用在测试类或者方法上,suite 套件可以理解成测试类的容器。

下边我们搭建一个TestNG测试框架,结合具体案例介绍一下它的功能。

核心依赖

引入 extentreports 和 testng

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <version>7.1.0</version> <scope>test</scope> </dependency> <dependency> <groupId>com.aventstack</groupId> <artifactId>extentreports</artifactId> <version>3.0.6</version> </dependency> </dependencies> TestNG 配置

TestNG 支持两种执行方式,第一种是用注解像 Junit 直接点方法名 run 执行。第二种配置 xml 文件的方式。

@Slf4j @Listeners({ExtentTestNGIReporterListener.class}) @SpringBootTest(classes=SpringbootTestngReportApplication.class) publicclassUserTestextendsAbstractTestNGSpringContextTests{ @Data classUser{ privateIntegeruserId; privateStringuserName; } /** *参数提供 */ @DataProvider(name="paramDataProvider") publicObject[][]paramDataProvider(){ Useruser1=newUser(); user1.setUserId(1); user1.setUserName("程序员内点事1"); Useruser2=newUser(); user2.setUserId(2); user2.setUserName("程序员内点事2"); returnnewObject[][]{{1,user1},{2,user2}}; } @Test(dataProvider="paramDataProvider") publicvoidqueryUser(Integerindex,Useruser){ if(index==2){ inta=1/0; } log.info("index:{},user:{}",index,JSON.toJSONString(user)); Assert.assertTrue(Objects.nonNull(user)); } }

xml 方式直接右键 .xml文件 run 就运行了。

<?xmlversion="1.0"encoding="UTF-8"?> <!DOCTYPEsuiteSYSTEM"http://testng.org/testng-1.0.dtd"> <suitename="用户单元测试"parallel="classes"thread-count="5"> <listeners> <listenerclass-name="com.xiaofu.report.config.ExtentTestNGIReporterListener"/> </listeners> <testverbose="1"name="用户测试"> <parametername="userId"value="1"/> <parametername="userName"value="程序员内点事"/> <groups> <definename="queryUser"/> <definename="queryUser1"/> </groups> <classes> <classname="com.xiaofu.report.UserTest"/> </classes> </test> </suite> 测试报告配置

手动配置一个测试报告侦听器类 ExtentTestNGIReporterListener,可以自行定义在测试报告上显示的数据,最后执行测试方法同时会生成测试报告。

/** *@authorxiaofu *@descriptionTestNg可视化配置 *@date2020/3/1916:44 */ publicclassExtentTestNGIReporterListenerimplementsIReporter{ //生成的路径以及文件名 privatestaticfinalStringOUTPUT_FOLDER="target/test-report/"; privatestaticfinalStringFILE_NAME="index.html"; privateExtentReportsextent; @Override publicvoidgenerateReport(List<XmlSuite>xmlSuites,List<ISuite>suites,StringoutputDirectory){ init(); booleancreateSuiteNode=false; if(suites.size()>1){ createSuiteNode=true; } for(ISuitesuite:suites){ Map<String,ISuiteResult>result=suite.getResults(); //如果suite里面没有任何用例,直接跳过,不在报告里生成 if(result.size()==0){ continue; } //统计suite下的成功、失败、跳过的总用例数 intsuiteFailSize=0; intsuitePassSize=0; intsuiteSkipSize=0; ExtentTestsuiteTest=null; //存在多个suite的情况下,在报告中将同一个一个suite的测试结果归为一类,创建一级节点。 if(createSuiteNode){ suiteTest=extent.createTest(suite.getName()).assignCategory(suite.getName()); } booleancreateSuiteResultNode=false; if(result.size()>1){ createSuiteResultNode=true; } for(ISuiteResultr:result.values()){ ExtentTestresultNode; ITestContextcontext=r.getTestContext(); if(createSuiteResultNode){ //没有创建suite的情况下,将在SuiteResult的创建为一级节点,否则创建为suite的一个子节点。 if(null==suiteTest){ resultNode=extent.createTest(r.getTestContext().getName()); }else{ resultNode=suiteTest.createNode(r.getTestContext().getName()); } }else{ resultNode=suiteTest; } if(resultNode!=null){ resultNode.getModel().setName(suite.getName() ":" r.getTestContext().getName()); if(resultNode.getModel().hasCategory()){ resultNode.assignCategory(r.getTestContext().getName()); }else{ resultNode.assignCategory(suite.getName(),r.getTestContext().getName()); } resultNode.getModel().setStartTime(r.getTestContext().getStartDate()); resultNode.getModel().setEndTime(r.getTestContext().getEndDate()); //统计SuiteResult下的数据 intpassSize=r.getTestContext().getPassedTests().size(); intfailSize=r.getTestContext().getFailedTests().size(); intskipSize=r.getTestContext().getSkippedTests().size(); suitePassSize =passSize; suiteFailSize =failSize; suiteSkipSize =skipSize; if(failSize>0){ resultNode.getModel().setStatus(Status.FAIL); } resultNode.getModel().setDescription(String.format("Pass:%s;Fail:%s;Skip:%s;",passSize,failSize,skipSize)); } buildTestNodes(resultNode,context.getFailedTests(),Status.FAIL); buildTestNodes(resultNode,context.getSkippedTests(),Status.SKIP); buildTestNodes(resultNode,context.getPassedTests(),Status.PASS); } if(suiteTest!=null){ suiteTest.getModel().setDescription(String.format("Pass:%s;Fail:%s;Skip:%s;",suitePassSize,suiteFailSize,suiteSkipSize)); if(suiteFailSize>0){ suiteTest.getModel().setStatus(Status.FAIL); } } } for(Strings:Reporter.getOutput()){ extent.setTestRunnerOutput(s); } extent.flush(); } privatevoidinit(){ //文件夹不存在的话进行创建 FilereportDir=newFile(OUTPUT_FOLDER); if(!reportDir.exists()&&!reportDir.isDirectory()){ reportDir.mkdirs(); } ExtentHtmlReporterhtmlReporter=newExtentHtmlReporter(OUTPUT_FOLDER FILE_NAME); //设置静态文件的DNS //怎么样解决cdn.rawgit.com访问不了的情况 htmlReporter.config().setResourceCDN(ResourceCDN.EXTENTREPORTS); htmlReporter.config().setDocumentTitle("用户服务自动化测试报告"); htmlReporter.config().setReportName("用户服务自动化测试报告"); htmlReporter.config().setChartVisibilityOnOpen(true); htmlReporter.config().setTestViewChartLocation(ChartLocation.TOP); htmlReporter.config().setTheme(Theme.STANDARD); htmlReporter.config().setEncoding("utf-8"); htmlReporter.config().setCSS(".node.level-1ul{display:none;}.node.level-1.activeul{display:block;}"); extent=newExtentReports(); extent.attachReporter(htmlReporter); extent.setReportUsesManualConfiguration(true); } privatevoidbuildTestNodes(ExtentTestextenttest,IResultMaptests,Statusstatus){ //存在父节点时,获取父节点的标签 String[]categories=newString[0]; if(extenttest!=null){ List<TestAttribute>categoryList=extenttest.getModel().getCategoryContext().getAll(); categories=newString[categoryList.size()]; for(intindex=0;index<categoryList.size();index ){ categories[index]=categoryList.get(index).getName(); } } ExtentTesttest; if(tests.size()>0){ //调整用例排序,按时间排序 Set<ITestResult>treeSet=newTreeSet<ITestResult>(newComparator<ITestResult>(){ @Override publicintcompare(ITestResulto1,ITestResulto2){ returno1.getStartMillis()<o2.getStartMillis()?-1:1; } }); treeSet.addAll(tests.getAllResults()); for(ITestResultresult:treeSet){ Object[]parameters=result.getParameters(); Stringname=""; //如果有参数,则使用参数的toString组合代替报告中的name for(Objectparam:parameters){ name =param.toString(); } if(name.length()==0){ name=result.getMethod().getMethodName(); } if(extenttest==null){ test=extent.createTest(name); }else{ //作为子节点进行创建时,设置同父节点的标签一致,便于报告检索。 test=extenttest.createNode(name).assignCategory(categories); } //test.getModel().setDescription(description.toString()); //test=extent.createTest(result.getMethod().getMethodName()); for(Stringgroup:result.getMethod().getGroups()) test.assignCategory(group); List<String>outputList=Reporter.getOutput(result); for(Stringoutput:outputList){ //将用例的log输出报告中 test.debug(output); } if(result.getThrowable()!=null){ test.log(status,result.getThrowable()); }else{ test.log(status,"Test" status.toString().toLowerCase() "ed"); } test.getModel().setStartTime(getTime(result.getStartMillis())); test.getModel().setEndTime(getTime(result.getEndMillis())); } } } privateDategetTime(longmillis){ Calendarcalendar=Calendar.getInstance(); calendar.setTimeInMillis(millis); returncalendar.getTime(); } }

会在指定的目录 target/test-report/ 下生成 index.html 测试报告文件,测试的成功率等信息显示的都比较直观,样式也还是蛮好看。

测试场景

下面就简单介绍几个我常用的 testNG 测试场景

1、参数化测试

使用 @DataProvider 注解为其他测试方法提供参数,queryUser 方法会执行 Object[][]数组中所有参数user1 、user2,相当于循环执行测试方法。

@DataProvider(name="paramDataProvider") publicObject[][]paramDataProvider(){ Useruser1=newUser(); user1.setUserId(1); user1.setUserName("程序员内点事1"); Useruser2=newUser(); user2.setUserId(2); user2.setUserName("程序员内点事2"); returnnewObject[][]{{1,user1},{2,user2}}; } @Test(dataProvider="paramDataProvider",groups="user") publicvoidqueryUser(Integerindex,Useruser){ log.info("index:{},user:{}",index,JSON.toJSONString(user)); }

xml 方式下还可以在配置文件设置参数

<parametername="name"value="程序员内点事"/>

@Test(groups="user") publicvoidqueryUser(Stringname){ log.info("我是测试方法~"); } 2、超时测试

可以给测试方法一个超时时间,如果实际执行时间超过设定的超时时间,用例将不通过。

@Test(timeOut=5000) publicvoidtimeOutTest()throwsInterruptedException{ Thread.sleep(6000); } 3、依赖测试

有时我们可能需要以特定顺序调用测试用例中的方法,或者希望在方法之间共享一些数据,TestNG支持在测试方法之间显式依赖的声明。

@Test publicvoidtoken(){ System.out.println("gettoken"); } @Test(dependsOnMethods={"token"}) publicvoidgetUser(){ System.out.println("thisistestgetUser"); } 总结

简单提了一下 TestNG 框架相关的知识,说实话本来就为给老铁弄个测试报告模板,一不留神说这么多。如果小伙伴们对这个测试框架感兴趣,下次我会出一份详细的 TestNG文章。

原创不易,燃烧秀发输出内容,如果有一丢丢收获,点个赞鼓励一下吧!

整理了几百本各类技术电子书相送 ,嘘~,「免费」 送给小伙伴们,私信或者评论【666】自行领取。和一些小伙伴们建了一个技术交流群,一起探讨技术、分享技术资料,旨在共同学习进步。

,
大家还看了
也许喜欢
更多栏目

© 1998-2024 shitiku.com.cn,All Rights Reserved.