TestNG教程
1、快速入门
1.1、TestNG是什么?
TestNG的定义:
- TestNG是一个测试框架,其灵感来自JUnit和NUnit,但引入了一些新的功能,使其功能更强大,使用更方便。
- TestNG是一个开源自动化测试框架。
- TestNG消除了大部分的旧框架的限制,使开发人员能够编写更加灵活和强大的测试。因为它在很大程度上借鉴了Java注解(JDK5.0引入的)来定义测试,它也可以显示如何使用这个新功能在真实的Java语言生产环境中。
TestNG的特点:
- 注解
- TestNG使用Java和面向对象的功能
- 支持综合类测试(例如,默认情况下,不用创建一个新的测试每个测试方法的类的实例)
- 独立的编译时测试代码和运行时配置/数据信息
- 灵活的运行时配置
- 主要介绍“测试组”
- 支持依赖测试方法,并行测试,负载测试,局部故障
- 灵活的插件API
- 支持多线程测试
1.2、入门示例
TestNG下载并安装:
- 下载:https://testng.org/doc/download.html
- 插件安装地址
- 对于Eclipse 3.4及更高版本,请输入 http://beust.com/eclipse
- 对于Eclipse 3.3及更低版本,请输入 http://beust.com/eclipse1
示例:
- 在 pom.xml 中添加依赖
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.8.7</version>
<scope>test</scope>
</dependency>
- 创建TestNg类
package com.test.testng;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import org.testng.annotations.Test;
public class TestHelloWorld {
@Test
public void test() {
RandomEmailGenerator randomEmailGenerator = new RandomEmailGenerator();
String email = randomEmailGenerator.generate();
assertNotNull(email);
assertEquals(email, "feedback@test.com");
}
}
//创建一个简单的Java类,它在方法中返回一个固定电子邮件
package com.test.testng;
public class RandomEmailGenerator {
public String generate() {
return "feedback@test.com";
}
}
2、注解介绍
2.1、基本注解
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 | 将类或方法标记为测试的一部分。 |
使用注解的好处:
- TestNG通过查找注释/注解来识别它感兴趣的方法。
- 可以将其他参数传递给注释。
- 注释是强类型的,所以编译器会马上标记任何错误。
- 测试类不再需要扩展任何东西(如TestCase,对于JUnit3)。
3、特色功能
3.1、预期异常测试
- 运行时异常
//如果divisionWithException()方法抛出一个运行时异常 — ArithmeticException,它会获得通过。
public class TestRuntime {
@Test(expectedExceptions=ArithmeticException.class)
public void divisionWithException() {
int i = 1/0;
System.out.println("After division the value of i is :"+ i);
}
}
- 检查异常
public class OrderBo {
public void save(Order order) throws OrderSaveException {
if(order == null) {
throw new OrderSaveException("Order is empty!");
}
}
public void update(Order order) throws OrderUpdateException,OrderNotFoundException {
if(order == null) {
throw new OrderUpdateException("Order is empty!");
}
throw new OrderNotFoundException("Order is not exists");
}
}
//自定义异常
public class OrderSaveException extends Exception{
public OrderSaveException(String string) {
// TODO Auto-generated constructor stub
}
}
//预期抛出自定义异常
@Test(expectedExceptions=OrderSaveException.class)
public void throwIfOrderIsNull() throws OrderSaveException {
orderBo.save(data);
}
@Test(expectedExceptions = { OrderUpdateException.class, OrderNotFoundException.class })
public void throwIfOrderIsNotExists() throws OrderUpdateException, OrderNotFoundException {
orderBo.update(data);
}
3.2、忽略测试
- 我们编写的代码并没有准备就绪,忽略没有准备好的测试代码;
- 注释
@Test(enabled = false)
有助于禁用此类的测试用例。
public class TestIgnore {
@Test
public void test1() {
System.out.println("测试方法1");
}
//忽略测试
@Test(enabled = false)
public void test2() {
System.out.println("测试方法2");
}
@Test
public void test3() {
System.out.println("测试方法3");
}
}
3.2、超时测试
- 超时”表示如果单元测试花费的时间超过指定的毫秒数,那么TestNG将会中止它并将其标记为失败。
public class TestTimeout {
//PASS
@Test(timeOut = 5000)
public void test1() throws InterruptedException{
Thread.sleep(4000);
}
//超过4000ms后自动标记失败
@Test(timeOut = 4000)
public void test2() {
while(true) {
//do nothing
}
}
}
3.4、分组测试
-
分组测试是TestNG中的一个新的创新功能,它在 JUnit框架中是不存在的。 它允许您将方法调度到适当的部分,并执行复杂的测试方法分组。
-
使用
<groups>
标记在testng.xml
文件中指定分组。 它可以在<test>
或<suite>
标签下找到。<suite>
标签中指定分组适用于其下的所有<test>
标签。
1、在方法上分组:
public class GroupsTest {
@BeforeGroups("database")
public void setupDB() {
System.out.println("setupDB()");
}
@AfterGroups("database")
public void cleanDB() {
System.out.println("cleanDB()");
}
@Test(groups = "selenium-test")
public void runSelenium() {
System.out.println("runSelenium()");
}
@Test(groups = "selenium-test")
public void runSelenium1() {
System.out.println("runSelenium()1");
}
@Test(groups = "database")
public void testConnectOracle() {
System.out.println("testConnectOracle()");
}
@Test(groups = "database")
public void testConnectMsSQL() {
System.out.println("testConnectMsSQL");
}
@Test(dependsOnGroups = { "database", "selenium-test" })
public void runFinal() {
System.out.println("runFinal");
}
}
2、在类上的分组
//注解在类上面
@Test(groups = "selenium-test")
public class TestSelenium {
public void runTest1() {
System.out.println("测试runTest1");
}
public void runTest2() {
System.out.println("测试runTest2");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="TestAll" parallel="false">
<test name="case1">
<classes>
<class name="com.test.testng.TestSelenium" />
<class name="com.test.testng.GroupsTest" />
</classes>
</test>
<test name="case2">
<groups>
<run>
<include name="selenium-test" />
</run>
</groups>
<classes>
<class name="com.test.testng.TestSelenium" />
<class name="com.test.testng.GroupsTest" />
</classes>
</test>
</suite>
3、其他分组
//测试方法也可以属于多个分组
@Test(groups = {"mysql","database"})
public void testConnectMsSQL() {
System.out.println("testConnectMsSQL");
}
3.5、套件测试
- 测试套件是用于测试软件程序的行为或一组行为的测试用例的集合。
- 在TestNG中,我们无法在测试源代码中定义一个套件,但它可以由一个XML文件表示,因为套件是执行的功能。
<suite>
是testng.xml
的根标记。 它描述了一个测试套件,它又由几个<test>
部分组成。
下表列出了< suite >接受的所有定义的合法属性:
属性 | 描述 |
---|---|
name | 套件的名称,这是一个强制属性。< suite name = “TestSuite”> |
verbose | 运行的级别或详细程度。 |
parallel | TestNG是否运行不同的线程来运行这个套件。 |
thread-count | 如果启用并行模式(忽略其他方式),则要使用的线程数。 |
annotations | 在测试中使用的注释类型。 |
time-out | 在本测试中的所有测试方法上使用的默认超时。 |
1、测试例子:
//测试类1:
public class TestConfig {
@BeforeSuite
public void testBeforeSuite() {
System.out.println("testBeforeSuite");
}
@AfterSuite
public void testAfterSuite() {
System.out.println("testAfterSuite");
}
@BeforeTest
public void testBeforeTest() {
System.out.println("testBeforeTest");
}
@AfterTest
public void testAfterTest() {
System.out.println("testAfterTest");
}
}
//测试类2
public class TestDatabase {
@Test(groups = "db")
public void testConnectOracle() {
System.out.println("testConnectOracle()");
}
@Test(groups = "db")
public void testConnectMsSQL() {
System.out.println("testConnectMsSQL");
}
@Test(groups = "db-nosql")
public void testConnectMongoDB() {
System.out.println("testConnectMongoDB");
}
@Test(groups = { "db", "brokenTests" })
public void testConnectMySQL() {
System.out.println("testConnectMySQL");
}
}
//测试类3
public class TestOrder {
@Test(groups = { "orderBo", "save" })
public void testMakeOrder() {
System.out.println("testMakeOrder");
}
@Test(groups = { "orderBo", "save" })
public void testMakeEmptyOrder() {
System.out.println("testMakeEmptyOrder");
}
@Test(groups = "orderBo")
public void testUpdateOrder() {
System.out.println("testUpdateOrder");
}
@Test(groups = "orderBo")
public void testFindOrder() {
System.out.println("testFindOrder");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="TestAll" parallel="false">
<test name="order">
<classes>
<class name="com.test.testng.TestConfig" />
<class name="com.test.testng.TestOrder" />
</classes>
</test>
<test name="database">
<classes>
<class name="com.test.testng.TestConfig" />
<class name="com.test.testng.TestDatabase" />
</classes>
</test>
</suite>
2、其他例子:
- 指定包名称而不是类名称
<suite name="TestAll">
<test name="order">
<packages>
<package name="com.test.testng.*" />
</packages>
</test>
</suite>
- 指定包含或排除的方法
<?xml version="1.0" encoding="UTF-8"?>
<suite name="TestAll">
<test name="order">
<classes>
<class name="com.test.testng.TestConfig" />
<class name="com.test.testng.TestOrder">
<methods>
<include name="testMakeOrder" />
<include name="testUpdateOrder" />
<!--
<exclude name="testMakeOrder" />
-->
</methods>
</class>
</classes>
</test>
</suite>
- 指定要包括或排除某个分组
<?xml version="1.0" encoding="UTF-8"?>
<suite name="TestAll">
<test name="database">
<groups>
<run>
<exclude name="brokenTests" />
<include name="db" />
</run>
</groups>
<classes>
<class name="com.test.testng.TestDatabase" />
</classes>
</test>
</suite>
3.6、依赖测试
- 有时,我们可能需要以特定顺序调用测试用例中的方法,或者可能希望在方法之间共享一些数据和状态。 TestNG支持这种依赖关系,因为它支持在测试方法之间显式依赖的声明。
- 在TestNG中,我们使用
dependOnMethods
和dependsOnGroups
来实现依赖测试。
1、dependOnMethods示例
// 1.1. 如果method1()通过,那么将执行method2()。
public class TestDedenpOnMethods {
@Test
public void method1() {
System.out.println("测试方法:method1()");
}
@Test(dependsOnMethods = "method1")
public void method2() {
System.out.println("测试方法:method2()");
}
}
// 1.2. 如果method1()失败,则将跳过method2()。
public class TestDedenpOnMethods {
@Test
public void method1() {
System.out.println("测试方法:method1()");
throw new RuntimeException();
}
@Test(dependsOnMethods = "method1")
public void method2() {
System.out.println("测试方法:method2()");
}
}
2、dependsOnGroups示例
//测试代码1
public class TestServer {
@Test(groups = "deploy")
public void deployServer() {
System.out.println("Deploying Server...");
}
@Test(groups = "deploy",dependsOnMethods = "deployServer")
public void deployBackUpServer() {
System.out.println("Deploying Backup Server...");
}
}
//测试代码2
public class TestApp {
@Test(dependsOnGroups = { "deploy", "db2" })
public void method1() {
System.out.println("This is method1");
}
@Test(dependsOnMethods = "method1")
public void method2() {
System.out.println("This is method2");
}
}
//测试代码3
public class TestDatabase2 {
@Test(groups = "db2", dependsOnGroups = "deploy")
public void initDB() {
System.out.println("This is initDB()");
}
@Test(groups = "db2", dependsOnMethods = { "initDB" })
public void testConnection() {
System.out.println("This is testConnection()");
}
}
3.7、参数化测试
- 在大多数情况下,会遇到业务逻辑需要大量测试的场景。 参数化测试允许开发人员使用不同的值一次又一次地运行相同的测试。
- TestNG可以通过两种不同的方式将参数直接传递给测试方法:
- 使用
testng.xml
- 使用数据提供者
- 使用
1、使用XML传递参数
filename
属性从testng.xml
传递,并通过@Parameters
注入到该方法中。
@Test
@Parameters({ "dbconfig", "poolsize" })
public void createConnection(String dbconfig, int poolsize) {
...
Properties prop = new Properties();
prop.load(new FileInputStream(dbconfig));
}
<?xml version="1.0" encoding="UTF-8"?>
<suite name="test-parameter">
<test name="example1">
<parameter name="dbconfig" value="db.properties" />
<parameter name="poolsize" value="10" />
<classes>
<class name="com.yiibai.TestParameterXML" />
</classes>
</test>
</suite>
2、通过@DataProvider传递参数
- 基本数据类型:Object[ ] [ ]
// 传入基本数据类型
@Test(dataProvider = "provideNumbers")
public void test(int number, int expected) {
Assert.assertEquals(number + 10, expected);
}
@DataProvider(name = "provideNumbers")
public Object[][] provideData() {
return new Object[][] { { 10, 20 }, { 100, 110 }, { 200, 210 } };
}
- 引用数据类型:对象参数,Map等等
// 传入Map
@Test(dataProvider = "dbconfig")
public void testConnection(Map<String, String> map) {
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println("[Key] : " + entry.getKey() + " [Value] : " + entry.getValue());
}
@DataProvider(name = "dbconfig")
public Object[][] provideDbConfig() {
Map<String, String> map = readDbConfig();
return new Object[][] { { map } };
}
3、@DataProvider + 方法
- 根据测试方法名称传递不同的参数。
@Test(dataProvider = "dataProvider")
public void test1(int number, int expected) {
Assert.assertEquals(number, expected);
}
@Test(dataProvider = "dataProvider")
public void test2(String email, String expected) {
Assert.assertEquals(email, expected);
}
@DataProvider(name = "dataProvider")
public Object[][] provideData(Method method) {
Object[][] result = null;
if (method.getName().equals("test1")) {
result = new Object[][] {
{ 1, 1 }, { 200, 200 }
};
} else if (method.getName().equals("test2")) {
result = new Object[][] {
{ "test@gmail.com", "test@gmail.com" },
{ "test@yahoo.com", "test@yahoo.com" }
};
}
return result;
}
-
@DataProvider + ITestContext
-
在TestNG中,我们可以使用
org.testng.ITestContext
来确定调用当前测试方法的运行时参数。
@Test(dataProvider = "dataProvider", groups = {"groupA"})
public void test1(int number) {
Assert.assertEquals(number, 1);
}
@Test(dataProvider = "dataProvider", groups = "groupB")
public void test2(int number) {
Assert.assertEquals(number, 2);
}
@DataProvider(name = "dataProvider")
public Object[][] provideData(ITestContext context) {
Object[][] result = null;
//get test name
//System.out.println(context.getName());
for (String group : context.getIncludedGroups()) {
System.out.println("group : " + group);
if ("groupA".equals(group)) {
result = new Object[][] { { 1 } };
break;
}
}
if (result == null) {
result = new Object[][] { { 2 } };
}
return result;
}