Yesterday, a friend of mine asked me if I had any ready-made test report template. Since I was too busy yesterday, I didn’t bother to read it, so I will make it up if I have time. As long as I have time, I will do everything WITHIN my power for everyone. However, my friends will understand more if I cannot do it because of my limited ability.
Junit unit testing is one of the most commonly used development tests when developing interfaces, and most of the time testing is just to see if the interface returns results properly. TestNG is much easier to implement when you need to test specific scenarios, such as interface timeout tests. It is very similar to JUnit, as long as you get the hang of it after using it.
Just to give you an ideaTestNG
Several important concepts of,@Test
The annotation annotation method is the smallest unit of execution that we can divide these individual test cases intogroup
Group management,group
You can use it to test classes or methods,suite
A suite can be thought of as a container for test classes.
Here we build a TestNG test framework, combined with a specific case to introduce its functions.
The core depends on
Introduce extentReports and 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>
Copy the code
TestNG configuration
TestNG supports two execution methods. The first is to use annotations like Junit to directly hit the method name run. The second way to configure XML files.
@Slf4j
@Listeners({ExtentTestNGIReporterListener.class})
@SpringBootTest(classes = SpringbootTestngReportApplication.class)
public class UserTest extends AbstractTestNGSpringContextTests {
@Data
class User {
private Integer userId;
private String userName;
}
/** * provides */
@DataProvider(name = "paramDataProvider")
public Object[] []paramDataProvider() {
User user1 = new User();
user1.setUserId(1);
user1.setUserName("Programmer matters 1");
User user2 = new User();
user2.setUserId(2);
user2.setUserName("Programmer matters 2");
return new Object[] [] {{1, user1}, {2, user2}};
}
@Test(dataProvider = "paramDataProvider")
public void queryUser(Integer index, User user) {
if (index == 2) {
int a = 1 / 0;
}
log.info("Index: {}, user: {}", index, JSON.toJSONString(user)); Assert.assertTrue(Objects.nonNull(user)); }}Copy the code
XML mode directly right-click the.xml file run to run.
<? xml version="1.0" encoding="UTF-8"? > <! DOCTYPE suite SYSTEM"http://testng.org/testng-1.0.dtd">
<suite name="User unit Tests" parallel="classes" thread-count="5">
<listeners>
<listener class-name="com.xiaofu.report.config.ExtentTestNGIReporterListener"/>
</listeners>
<test verbose="1" name="User tests">
<parameter name="userId" value="1"/>
<parameter name="userName" value="Programmer internal matters."/>
<groups>
<define name="queryUser"/>
<define name="queryUser1"/>
</groups>
<classes>
<class name="com.xiaofu.report.UserTest"/>
</classes>
</test>
</suite>
Copy the code
Test Report Configuration
Manually configure a test report listener class ExtentTestNGIReporterListener, defined can be displayed on the test report of the data, finally test execution method generates test report at the same time.
/ * * *@author xiaofu
* @description TestNg Visual configuration *@date 2020/3/19 16:44 * /
public class ExtentTestNGIReporterListener implements IReporter {
// The generated path and file name
private static final String OUTPUT_FOLDER = "target/test-report/";
private static final String FILE_NAME = "index.html";
private ExtentReports extent;
@Override
public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) {
init();
boolean createSuiteNode = false;
if (suites.size() > 1) {
createSuiteNode = true;
}
for (ISuite suite : suites) {
Map<String, ISuiteResult> result = suite.getResults();
// If suite doesn't have any use cases, skip them and don't generate them in the report
if (result.size() == 0) {
continue;
}
// Count the total number of successful, failed, skipped cases under Suite
int suiteFailSize = 0;
int suitePassSize = 0;
int suiteSkipSize = 0;
ExtentTest suiteTest = null;
// In the case of multiple suites, group the test results of the same suite in the report and create tier 1 nodes.
if (createSuiteNode) {
suiteTest = extent.createTest(suite.getName()).assignCategory(suite.getName());
}
boolean createSuiteResultNode = false;
if (result.size() > 1) {
createSuiteResultNode = true;
}
for (ISuiteResult r : result.values()) {
ExtentTest resultNode;
ITestContext context = r.getTestContext();
if (createSuiteResultNode) {
If suite is not created, it will be created as a level 1 node in SuiteResult, otherwise it will be created as a child node of 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());
// Collect data from SuiteResult
int passSize = r.getTestContext().getPassedTests().size();
int failSize = r.getTestContext().getFailedTests().size();
int skipSize = 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 (String s : Reporter.getOutput()) {
extent.setTestRunnerOutput(s);
}
extent.flush();
}
private void init() {
// Create folder if it does not exist
File reportDir = new File(OUTPUT_FOLDER);
if(! reportDir.exists() && ! reportDir.isDirectory()) { reportDir.mkdirs(); } ExtentHtmlReporter htmlReporter =new ExtentHtmlReporter(OUTPUT_FOLDER + FILE_NAME);
// Set DNS for static files
// How to solve the problem that cdn.rawgit.com cannot be accessed
htmlReporter.config().setResourceCDN(ResourceCDN.EXTENTREPORTS);
htmlReporter.config().setDocumentTitle("User Service Automation Test Report");
htmlReporter.config().setReportName("User Service Automation Test Report");
htmlReporter.config().setChartVisibilityOnOpen(true);
htmlReporter.config().setTestViewChartLocation(ChartLocation.TOP);
htmlReporter.config().setTheme(Theme.STANDARD);
htmlReporter.config().setEncoding("utf-8");
htmlReporter.config().setCSS(".node.level-1 ul{ display:none; } .node.level-1.active ul{display:block; }");
extent = new ExtentReports();
extent.attachReporter(htmlReporter);
extent.setReportUsesManualConfiguration(true);
}
private void buildTestNodes(ExtentTest extenttest, IResultMap tests, Status status) {
// Get the label of the parent node if there is a parent node
String[] categories = new String[0];
if(extenttest ! =null) {
List<TestAttribute> categoryList = extenttest.getModel().getCategoryContext().getAll();
categories = new String[categoryList.size()];
for (int index = 0; index < categoryList.size(); index++) {
categories[index] = categoryList.get(index).getName();
}
}
ExtentTest test;
if (tests.size() > 0) {
// Adjust the order of use cases by time
Set<ITestResult> treeSet = new TreeSet<ITestResult>(new Comparator<ITestResult>() {
@Override
public int compare(ITestResult o1, ITestResult o2) {
return o1.getStartMillis() < o2.getStartMillis() ? -1 : 1; }}); treeSet.addAll(tests.getAllResults());for (ITestResult result : treeSet) {
Object[] parameters = result.getParameters();
String name = "";
// Replace the name in the report with the toString combination of the parameters, if any
for (Object param : parameters) {
name += param.toString();
}
if (name.length() == 0) {
name = result.getMethod().getMethodName();
}
if (extenttest == null) {
test = extent.createTest(name);
} else {
// When created as a child node, set the same label as the parent node to facilitate report retrieval.
test = extenttest.createNode(name).assignCategory(categories);
}
//test.getModel().setDescription(description.toString());
//test = extent.createTest(result.getMethod().getMethodName());
for (String group : result.getMethod().getGroups())
test.assignCategory(group);
List<String> outputList = Reporter.getOutput(result);
for (String output : outputList) {
// Put the log output report of the use case
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()));
}
}
}
private Date getTime(long millis) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(millis);
returncalendar.getTime(); }}Copy the code
Will be in the specified directorytarget/test-report/
Next generationindex.html
Test report files, test success rate and other information are more intuitive, the style is still pretty good.
Test scenarios
Here are a few of the testNG scenarios I often use
1. Parameterized testing
Using the @dataProvider annotation to provide parameters for the other test methods, the queryUser method executes all the parameters user1 and user2 in the Object[][] array, which is equivalent to executing the test method in a loop.
@DataProvider(name = "paramDataProvider")
public Object[] []paramDataProvider() {
User user1 = new User();
user1.setUserId(1);
user1.setUserName("Programmer matters 1");
User user2 = new User();
user2.setUserId(2);
user2.setUserName("Programmer matters 2");
return new Object[] [] {{1, user1}, {2, user2}};
}
@Test(dataProvider = "paramDataProvider",groups = "user")
public void queryUser(Integer index, User user) {
log.info("Index: {}, user: {}", index, JSON.toJSONString(user));
}
Copy the code
In XML mode, you can also set parameters in the configuration file
<parameter name="name" value="Programmer internal matters."/>
Copy the code
@Test(groups = "user")
public void queryUser(String name) {
log.info("I am the test method ~");
}
Copy the code
2. Timeout tests
A test method can be given a timeout, and if the actual execution time exceeds the specified timeout, the use case will fail.
@Test(timeOut = 5000)
public void timeOutTest() throws InterruptedException {
Thread.sleep(6000);
}
Copy the code
3. Dependency testing
TestNG supports declarations of explicit dependencies between test methods when you need to call methods in a test case in a particular order, or when you want to share some data between methods.
@Test
public void token() {
System.out.println("get token");
}
@Test(dependsOnMethods= {"token"})
public void getUser() {
System.out.println("this is test getUser");
}
Copy the code
conclusion
TestNG framework: TestNG framework: TestNG framework: TestNG framework: TestNG framework If you are interested in this framework, I will publish a detailed TestNG article next time.
Original is not easy, burning hair output content, if there is a lost harvest, a praise to encourage it!
I sorted out hundreds of technical e-books and gave them to my friends. Pay attention to the public number reply [666] to get yourself. I set up a technology exchange group with some friends to discuss technology and share technical information, aiming to learn and progress together. If you are interested, please join us!