TestNG 安装、常用注解、断言、参数化、生成测试报告、测试套件、执行Excel测试用例
1、安装 testng
1)离线安装
(先断网)帮助→安装新软件→添加,位置选择 achive,选择 zip 文件即可。
插件:
链接:https://pan.baidu.com/s/1-HUUvDV02w2gAYKFS_7_TQ
提取码:nd35
2)、网络安装
帮 助 → 安 装 新 软 件 → 联 网 安 装 testing , Work with 下 输 入 : http://beust.com/eclipse→添加→勾选 TestNG
2、常用注解
@BeforeSuite/@AfterSuite:在某个测试套件开始之前运行 / 在某个测试套件开始之后运行
@BeforeTest/@AfterTest:在某个测试(test)开始之前运行 / 在某个测试(test)开始之后运行
@BeforeClass/@AfterClass : 在某个测试类的开始之前运行 / 在某个测试类的开始之后运行。
@BeforeMethod / @AfterMethod : 在某个测试方法的开始之前运行 / 在某个测试方法的开始之后运行。

package com02; import org.testng.Assert; import org.testng.annotations.*; public class TestTestNG01 { @BeforeSuite public void test01(){ System.out.println("--BeforeSuite"); } @AfterSuite public void test03(){ System.out.println("--AfterSuite"); } @BeforeTest public void test04(){ System.out.println("--BeforeTest"); } @AfterTest public void test05(){ System.out.println("--AfterTest"); } @BeforeClass public void test06(){ System.out.println("--BeforeClass"); } @AfterClass public void test07(){ System.out.println("--AfterClass"); } @BeforeMethod public void test08(){ System.out.println("--BeforeMethod"); } @AfterMethod public void test09(){ System.out.println("--AfterMethod"); } @Test public void test02(){ Assert.assertEquals(1,1); System.out.println("'------------------------------'"); } }

D:\02tools\java\bin\java.exe -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\02tools\IntelliJ IDEA 2020.2.2\lib\idea_rt.jar=62163:D:\02tools\IntelliJ IDEA 2020.2.2\bin" -Dfile.encoding=UTF-8 -classpath "D:\02tools\IntelliJ IDEA 2020.2.2\lib\idea_rt.jar;D:\02tools\IntelliJ IDEA 2020.2.2\plugins\testng\lib\testng-rt.jar;D:\02tools\java\jre\lib\charsets.jar;D:\02tools\java\jre\lib\deploy.jar;D:\02tools\java\jre\lib\ext\access-bridge-64.jar;D:\02tools\java\jre\lib\ext\cldrdata.jar;D:\02tools\java\jre\lib\ext\dnsns.jar;D:\02tools\java\jre\lib\ext\jaccess.jar;D:\02tools\java\jre\lib\ext\jfxrt.jar;D:\02tools\java\jre\lib\ext\localedata.jar;D:\02tools\java\jre\lib\ext\nashorn.jar;D:\02tools\java\jre\lib\ext\sunec.jar;D:\02tools\java\jre\lib\ext\sunjce_provider.jar;D:\02tools\java\jre\lib\ext\sunmscapi.jar;D:\02tools\java\jre\lib\ext\sunpkcs11.jar;D:\02tools\java\jre\lib\ext\zipfs.jar;D:\02tools\java\jre\lib\javaws.jar;D:\02tools\java\jre\lib\jce.jar;D:\02tools\java\jre\lib\jfr.jar;D:\02tools\java\jre\lib\jfxswt.jar;D:\02tools\java\jre\lib\jsse.jar;D:\02tools\java\jre\lib\management-agent.jar;D:\02tools\java\jre\lib\plugin.jar;D:\02tools\java\jre\lib\resources.jar;D:\02tools\java\jre\lib\rt.jar;D:\java\test\testProject2\target\classes;D:\repository\mavenRep\org\dom4j\dom4j\2.1.1\dom4j-2.1.1.jar;D:\repository\mavenRep\jaxen\jaxen\1.2.0\jaxen-1.2.0.jar;D:\repository\mavenRep\org\testng\testng\7.4.0\testng-7.4.0.jar;D:\repository\mavenRep\com\beust\jcommander\1.78\jcommander-1.78.jar;D:\repository\mavenRep\org\webjars\jquery\3.5.1\jquery-3.5.1.jar;D:\02tools\IntelliJ IDEA 2020.2.2\plugins\testng\lib\jcommander-1.27.jar" com.intellij.rt.testng.RemoteTestNGStarter -usedefaultlisteners false -socket62162 @w@C:\Users\liyujiao\AppData\Local\Temp\idea_working_dirs_testng.tmp -temp C:\Users\liyujiao\AppData\Local\Temp\idea_testng.tmp --BeforeSuite --BeforeTest --BeforeClass --BeforeMethod '------------------------------' --AfterMethod --AfterClass --AfterTest --AfterSuite =============================================== Default Suite Total tests run: 1, Passes: 1, Failures: 0, Skips: 0 =============================================== Process finished with exit code 0
5) @Test(属性 1=值 1,属性 2=值 2,...)
@Test(description="订票",priority=2,dataProvider="getParam",dataProviderClass=getParam所在类的类名.class )
public void book(String cong , String dao){
6) @DataProvider(name="参数集名")
用来修饰获得参数的方法,此方法用于生成测试数据
3、断言
1) Assert.assertTrue(boolean 型结果)
2) Assert.assertEquals(实际结果,预期结果)
4、参数化
@Parameters
运行入口testng.xml文件
package com02; import org.testng.annotations.Parameters; import org.testng.annotations.Test; public class Test02 { //读取配置文件中参数 @Test @Parameters(value = {"type","num"}) public void test01(String type,String num){ System.out.println("type="+type+",num="+num); } }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"> <suite name="All Test Suite"> <test verbose="2" preserve-order="true" name="D:/java/test/testProject2"> <classes> <class name="com02.Test02"> <methods> <include name="test01"/> </methods> <parameter name="type" value="fox"/> <parameter name="num" value="2"/> </class> </classes> </test> </suite>
@DataProvider(name="测试数据集名")
name 项可省略,括号无所谓
例如
1) 创建获取参数的方法 @DataProvider public Object[][] getParam() throws Exception{ Object data[][]={ {"London","Paris"},{"Denver","London"},{"Paris","Denver"} }; return data; } 2)使用 dataProvider 访问参数 @Test(description="订票",priority=2,dataProvider="getParam") public void book(String cong , String dao){ new Select(drv.findElement(By.name("depart"))).selectByVisibleText(cong); new Select(drv.findElement(By.name("arrive"))).selectByVisibleText(dao); Boolean rs=drv.findElement(By.tagName("body")).getText().contains("leaves "+cong+" for "+dao); assertTrue(rs); drv.findElement(By.name("Book Another")).click(); }
方式二:分离获得参数的类,使用列表获取文件中的参数
1)定义获得参数的类
@DataProvider
public Object[][] getParam() throws Exception{
List<String[]> rows=new ArrayList<String[]>();
File file=new File("C:/.../flights.txt");
FileReader reader=new FileReader(file);
BufferedReader buffer=new BufferedReader(reader);
String row=null;
while((row=buffer.readLine())!=null){
String columns[]=row.split("\t");
rows.add(columns);
}
reader.close();
Object[][] data=new Object[rows.size()][];
for(int i=0;i<rows.size();i++)
data[i]=rows.get(i);
return data;
}
2)引用参数类中的参数
@Test(description="订票",priority=2,dataProvider="getParam",dataProviderClass=getParam所在类的类名.class )
public void book(String cong , String dao){
new Select(drv.findElement(By.name("depart"))).selectByVisibleText(cong);
new Select(drv.findElement(By.name("arrive"))).selectByVisibleText(dao);
drv.findElement(By.name("Book Another")).click();
}
依赖测试
不加依赖执行测试,顺序为:ASCII
加入依赖
忽略测试
enabled = false
超时测试
timeOut = 500
分组执行
1、测试方法指定groups
2、在testng.xml的test指定相应的测试类
3、在testng.xml的groups里面指定run的分组,或者exclude的分组
include
exculde
5 生成测试报告
测试报告默认存储位置 : Java 项目名\test-output
可以自己编写并优化 TestReport.java
例如:
TestRepost.class
package utils;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import org.testng.ITestContext;
import org.testng.ITestResult;
import org.testng.TestListenerAdapter;
public class TestReport extends TestListenerAdapter{
private String reportPath;
@Override
public void onStart(ITestContext context) {
File htmlReportDir = new File("test-output");
if (!htmlReportDir.exists()) {
htmlReportDir.mkdirs();
}
String reportName = formateDate()+"_result.html";
reportPath = htmlReportDir+"/"+reportName;
File report = new File(htmlReportDir,reportName);
if(report.exists()){
try {
report.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
StringBuilder sb = new StringBuilder("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />"
+ "<title>自动化测试报告</title></head><body style=\"background-color:#99FFCC;\">"
+ "<div id=\"top\" align=\"center\"><p style=\"font-weight:bold;\">测试用例运行结果列表</p>"
+ "<table width=\"90%\" height=\"80\" border=\"1\" align=\"center\" cellspacing=\"0\" rules=\"all\" style=\"table-layout:relative;\">"
+ "<thead>"
+ "<tr>"
+ "<th>测试用例名</th>"
+ "<th>测试用例结果</th>"
+ "</tr>"
+ "</thead>"
+ "<tbody style=\"word-wrap:break-word;font-weight:bold;\" align=\"center\">");
String res = sb.toString();
try {
Files.write((Paths.get(reportPath)),res.getBytes("utf-8"));
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onTestSuccess(ITestResult result) {
StringBuilder sb = new StringBuilder("<tr><td>");
sb.append(result.getMethod().getRealClass()+"."+result.getMethod().getMethodName());
sb.append("</td><td><font color=\"green\">Passed</font></td></tr>");
String res = sb.toString();
try {
Files.write((Paths.get(reportPath)),res.getBytes("utf-8"),StandardOpenOption.APPEND);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onTestSkipped(ITestResult result) {
StringBuilder sb = new StringBuilder("<tr><td>");
sb.append(result.getMethod().getRealClass()+"."+result.getMethod().getMethodName());
sb.append("</td><td><font color=\"yellow\">Skipped</font>");
sb.append("<p align=\"left\">测试用例<font color=\"red\">跳过</font>,原因:<br>");
sb.append("<br><a style=\"background-color:#CCCCCC;\">");
Throwable throwable = result.getThrowable();
sb.append(throwable.getMessage());
sb.append("</a></p></td></tr>");
String res = sb.toString();
try {
Files.write((Paths.get(reportPath)),res.getBytes("utf-8"),StandardOpenOption.APPEND);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onTestFailure(ITestResult result) {
StringBuilder sb = new StringBuilder("<tr><td>");
sb.append(result.getMethod().getRealClass()+"."+result.getMethod().getMethodName());
sb.append("</td><td><font color=\"red\">Failed</font><br>");
sb.append("<p align=\"left\">测试用例执行<font color=\"red\">失败</font>,原因:<br>");
sb.append("<br><a style=\"background-color:#CCCCCC;\">");
Throwable throwable = result.getThrowable();
sb.append(throwable.getMessage());
sb.append("</a></p></td></tr>");
String res = sb.toString();
try {
Files.write((Paths.get(reportPath)),res.getBytes("utf-8"),StandardOpenOption.APPEND);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFinish(ITestContext testContext) {
StringBuilder sb = new StringBuilder("</tbody></table><a href=\"#top\">返回顶部</a></div></body>");
sb.append("</html>");
String msg = sb.toString();
try {
Files.write((Paths.get(reportPath)),msg.getBytes("utf-8"),StandardOpenOption.APPEND);
} catch (IOException e) {
e.printStackTrace();
}
}
public static String formateDate(){
SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd HHmmss");
Calendar cal = Calendar.getInstance();
Date date = cal.getTime();
return sf.format(date);
}
}
6、测试套件suite
一个 suite 可以包含多个独立的 test。
1)定义测试套件
2)修正testng.xml
3)运行测试套件
打开 testng.xml→点击工具栏运行按钮→选择“TestNG Suite”。
7、为测试套件中的测试方法传递参数
1)定义参数
2)引用参数
@Test @Parameters({ "参数名 1", "参数名 2" })
public void 测试方法(String 参数名 1){
}
testng.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"> <suite thread-count="2" name="ExplorerSuite" parallel="tests"> <test name="IETest"> <classes> <class name="com03.IETest"/> </classes> </test> <!-- IETest --> <test name="ChromeTest"> <pav rameter name="url" value="http://localhost:1080/webtours" /> <classes> <class name="com03.ChromeTest"/> </classes> </test> <!-- ChromeTest --> </suite> <!-- ExplorerSuite -->
引用参数
@Test(description="登录",priority=1,dataProvider="getParamByExcel",dataProviderClass=Param.class)
public void login(String username,String password,String expect) throws InterruptedException {
// 进入frame navbar
driver.switchTo().defaultContent();
driver.switchTo().frame("body");
driver.switchTo().frame("navbar");
// 识别元素
// 用户名
driver.findElement(By.name("username")).clear();
driver.findElement(By.name("username")).sendKeys(username);
// 密码
driver.findElement(By.name("password")).clear();
driver.findElement(By.name("password")).sendKeys(password);
// 点击登录
driver.findElement(By.name("login")).click();
Thread.sleep(4000);
testSuccess(driver,"body","info","登录结果验证:",expect);
}
8、执行 Excel 测试用例
1 )准备 Excel 测试用例
至少包括输入数据和预期结果
2 )读 Excel
首先导入 jxl.jar 包
package com02;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.testng.annotations.DataProvider;
import jxl.Cell;
import jxl.Sheet;
import jxl.Workbook;
import jxl.read.biff.BiffException;
public class Param {
@DataProvider public Object[][] getParamByExcel() throws IOException, BiffException{ InputStream file = new FileInputStream("C:\\Users\\blue\\Desktop\\fight.xls"); Workbook excel = Workbook.getWorkbook(file); Sheet sheet = excel.getSheet(0); List<String[]> list = new ArrayList<String[]>(); Cell cell = null; //0 表示第 1 行 for (int i=0; i<sheet.getRows();i++) { //创建数组存储一行的多列值 String[] rowi_col=new String[sheet.getColumns()]; for (int j= 0;j<sheet.getColumns();j++) { //获取第 i 行第 j 列的值 cell=sheet.getCell(j,i); rowi_col[j]=cell.getContents(); } // 把刚获取的列存入 list list.add(rowi_col); } file.close();
//返回 Object 对象作为参数 Object[][] data = new Object[list.size()][3]; for(int i=0;i<list.size();i++) { data[i] = list.get(i); } return data; }
}
3)断言失败时输出实际结果
断言失败时,断言语句后面的语句将停止运行
String result=driver.findElement(By.tagName("body")).getText(); Boolean loginResult=result.contains(expect); try{ Assert.assertTrue(loginResult); }catch(AssertionError e){ Assert.fail(result); }
完整代码展示
package com02; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.Listeners; import org.testng.annotations.Test; import utils.TestReport; //参数化,测试登录 @Listeners({TestReport.class}) public class TestWebtours05 { static WebDriver driver ; //打开网站 @BeforeClass public void before() throws Exception { // 打开浏览器 System.setProperty("webdriver.chrome.driver", "C:\\Users\\blue\\Desktop\\driver\\chromedriver.exe"); driver = new ChromeDriver(); // 打开网址 driver.get("http://localhost:1080/webtours"); } //每运行一个方法,自动休眠3S @AfterMethod public void sleep() throws InterruptedException { Thread.sleep(3000); } //登录 @Test(description="登录",priority=1,dataProvider="getParamByExcel",dataProviderClass=Param.class) public void login(String username,String password,String expect) throws InterruptedException { // 进入frame navbar driver.switchTo().defaultContent(); driver.switchTo().frame("body"); driver.switchTo().frame("navbar"); // 识别元素 // 用户名 driver.findElement(By.name("username")).clear(); driver.findElement(By.name("username")).sendKeys(username); // 密码 driver.findElement(By.name("password")).clear(); driver.findElement(By.name("password")).sendKeys(password); // 点击登录 driver.findElement(By.name("login")).click(); Thread.sleep(4000); testSuccess(driver,"body","info","登录结果验证:",expect); } //关闭窗口 @AfterClass public void close() { //关闭窗口 driver.quit(); } //断言 public static void testSuccess(WebDriver driver,String location_1,String location_2,String message,String info) { driver.switchTo().defaultContent(); driver.switchTo().frame(location_1); driver.switchTo().frame(location_2); //判断是否加载网页成功 Boolean res = driver.findElement(By.tagName("body")).getText().contains(info); System.out.println("---------断言--------"+message+res+"\t"); try { Assert.assertTrue(res); }catch(AssertionError e) { //输出实际结果 Assert.fail(driver.findElement(By.tagName("body")).getText()); } } }
页面效果
1)登录
2)登陆成功
3)预订航班
4)选择班次
5)用户信息确认
6)预订成功
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~