文章目录
-
- 一、Eclipse 中安装 、使用TestNG
-
- 1、在线安装
- 2、离线安装
- 3、安装TestNG之后,导入jar
- 4、创建TestNG单元测试项目
- 二、TestNG 注释
-
- @Test 注解
- @Parameters 注解:用于为测试方法传递参数
- @DataProvider 注解
- @Factory 注解
- @Listeners 注解
- 三、testng.xml 文件
- 四、运行测试用例、查看测试结果
-
- TestNG 的运行测试用例有3种方法:
-
- (1)方法一:通过class来执行:
- (1)方法二
- (2)方法三
- 五、TestNG 测试技巧
-
- 1、依赖性测试
- 2、忽略测试
- 3、参数化测试
一、Eclipse 中安装 、使用TestNG
1、在线安装
- 首先打开 Eclipse,点击菜单栏:Help ->Install New Software;
- 点击“Add…”按钮,弹出 Add Repository 窗口,输入 name 和 location ;
- Name:TestNG
- location:http://beust.com/eclipse
- 然后,勾选 testNG,点击“Next”进行安装 ;
- 下载完成,点击“Next >” 根据提示完成安装 ;
2、离线安装
-
testNG官网:http://testng.org/doc/
点开之后点击“download”
-
点开链接: http://beust.com/eclipse
- 选择某一版本,点击下载;
- 将解压缩的文件夹放到eclipse的dropin目录下,重启eclipse;
- 打开Eclipse,新建-other,选择“TestNG——>TestNG Class”,有这个就成功了;
3、安装TestNG之后,导入jar
4、创建TestNG单元测试项目
new --> other --> TestNG --> 确定
二、TestNG 注释
API:http://testng.org/doc/documentation-main.html
|注 解| 描 述|
|-|
|@BeforeSuite|注解的方法将只运行一次,在该套件中所有测试运行之前运行|
|@AfterSuite |注解的方法将只运行一次,在此套件中运行所有测试之后运行|
|@BeforeTest |带注释的方法将在所有属于<test>
标记中的类的测试方法运行之前运行。|
|@AfterTest |带注释的方法将在所有属于<test>
标记内的类的测试方法运行之后运行。|
|@BeforeClass |在调用当前类中的第一个测试方法之前,将运行带注释的方法|
|@AfterClass |在运行当前类的所有测试方法之后,将运行带注释的方法|
|@BeforeGroups |这个配置的组列表方法将先运行。这个方法保证在第一个测试方法被调用之前运行。|
|@AfterGroups |这个配置的组列表方法将最后运行。这个方法保证在最后一个测试方法被调用之后运行。|
|@BeforeMethod |在每个测试方法之前运行。|
@AfterMethod | 在每个测试方法之后运行。 |
---|---|
@DataProvider | 标记方法为测试方法提供数据。带注释的方法必须返回一个对象[][],其中每个对象[]可以被分配到测试方法的参数列表。想要从这个DataProvider中接收数据的@Test方法需要使用DataProvider名称,它等于这个注释的名称。 |
@Factory | 标记一个方法作为一个工厂,返回将被TestNG用作测试类的对象。该方法必须返回对象[]。 |
@Listeners | 在测试类中定义侦听器。 |
@Parameters | 用于为测试方法传递参数 |
@Test | 标记一个类或方法作为测试的一部分。 |
上述的注解分为Before类别和After类,我们可以在Before类别的注解方法里面做一些初始化动作,如实例化数据库连接、新建数据库连接池、创建线程池、打开文件流等等;在After类别的注解方法里面做一些销毁动作,如释放数据库连接、销毁数据库连接池、销毁线程池或者关闭文件流等等。同一类别的不同注解会在不同的位置被调用;
beforeSuite-1
beforeSuite-2beforeTest-1beforeTest-2beforeClass-1beforeMethod-1test1-1afterMethod-1beforeMethod-1test2-1afterMethod-1afterClass-1beforeClass-2beforeMethod-2test1-2afterMethod-2beforeMethod-2test2-2afterMethod-2afterClass-2afterTest-1afterTest-2
afterSuite-1
afterSuite-1
我们可以根据自身需求,选择特定的位置去执行一些初始化动作/销毁动作,以及一些销毁动作:
- 假如你需要针对整个测试suite做初始化动作,那么应该选择在被@BeforeSuite注解的方法里面执行
- 如果需要针对一个
<test>
里面的所有测试类做初始化动作,那么可以选择在被@BeforeTest注解的方法里面执行 - 假如你想在每一个测试方法执行前做初始化动作,那么应该选择@BeforeMethod
@Test 注解
@Test 注解是TestNG的核心注解,被打上该注解的方法,表示为一个测试方法;
这个注解有多个配置属性,用法为:@Test(param1 = …, param2 = …)
|属性|描述|
|–|
|alwaysRun | 如果设置为true,这个测试方法将始终运行,即使它依赖的前置测试方法失败|
|dataProvider | 选定传入参数的构造器。此测试方法的数据提供程序的名称。|
|dataProviderClass |确定参数构造器的Class类。(参数构造器首先会在当前测试类里面查找,如果参数构造器不在当前测试类定义,那么必须使用该属性来执行它所在的Class类)|
|dependsOnGroups|确定依赖的前置测试组别。|
|dependsOnMethods | 确定依赖的前置测试方法。|
|description |测试方法描述信息。(建议为每个测试方法添加有意义的描述信息,这将会在最后的报告中展示出来)|
|enabled | 默认为true,如果指定为false,表示不执行该测试方法。|
|expectedExceptions |指定期待测试方法抛出的异常,多个异常以逗号(,)隔开。|
|groups| 指定该测试方法所属的组,可以指定多个组,以逗号隔开。组测试的用法将在后面文章单独介绍。|
|invocationCount | 指定测试方法需要被调用的次数。|
|invocationTimeOut| 每一次调用的超时时间,如果invocationCount没有指定,该参数会被忽略。应用场景可以为测试获取数据库连接,超时就认定为失败。单位是毫秒。|
|priority | 指定测试方法的优先级,数值越低,优先级越高,将会优先于其他数值高的测试方法被调用。(注意是针对一个测试类的优先级)|
|timeout | 指定整个测试方法的超时时间。单位是毫秒。|
@Parameters 注解:用于为测试方法传递参数
public class AnnotationParametersTest {@Parameters(value = {"param1", "param2"})@Testpublic void test(String arg1, String arg2) {System.out.println("use @Parameters to fill method arguments : arg 1 = " + arg1 + ", arg2 = " + arg2);}
}
testng.xml配置
<test name="testAnnotationParameters"><parameter name="param1" value="value1"></parameter><parameter name="param2" value="value2"></parameter><classes><class name="com.crazypig.testngdemo.AnnotationParametersTest" /></classes>
</test>
输出结果:
use @Parameters to fill method arguments : arg 1 = value1, arg2 = value2
@DataProvider 注解
@Parameters注解可以为测试方法传递参数,但是这种方式参数值需要配置在testng.xml里面,灵活性不高。而**@DataProvider注解**同样可以为测试方法传递参数值,并且,它是真正意义上的参数构造器,可以传入多组测试数据对测试方法进行测试。被@DataProvider注解的方法,方法返回值必须为Object[][]或者Iterator<Object[]>。
public class AnnotationDataProviderTest {@DataProvider(name="testMethodDataProvider")public Object[][] testMethodDataProvider() { //方法返回值必须为Object[][]或者Iterator<Object[]>return new Object[][]{
{"value1-1", "value2-1"}, {"value1-2", "value2-2"}, {"value1-3", "value2-3"}};}@Test(dataProvider="testMethodDataProvider")public void test(String arg1, String arg2) {System.out.println("use @DataProvider to fill method argument : arg1 = " + arg1 + " , arg2 = " + arg2);}
}
testng.xml配置:
<test name="testDataProvider"><classes><class name="com.crazypig.testngdemo.AnnotationDataProviderTest" /></classes>
</test>
运行结果:
use @DataProvider to fill method argument : arg1 = value1-1 , arg2 = value2-1
use @DataProvider to fill method argument : arg1 = value1-2 , arg2 = value2-2
use @DataProvider to fill method argument : arg1 = value1-3 , arg2 = value2-3
@Factory 注解
在一个方法上面打上@Factory注解,表示该方法将返回能够被TestNG测试的测试类。利用了设计模式中的工厂模式。
public class AnnotationFactoryTest {@Factorypublic Object[] getSimpleTest() {return new Object[]{ new SimpleTest("one"), new SimpleTest("two")};}
}
public class SimpleTest {private String param;public SimpleTest(String param) {this.param = param;}@Testpublic void test() {System.out.println("SimpleTest.param = " + param);}
}
testng.xml配置:
<test name="testFactory"><classes><class name="com.crazypig.testngdemo.AnnotationFactoryTest" /></classes>
</test>
运行结果:
SimpleTest.param = one
SimpleTest.param = two
@Listeners 注解
一般我们写测试类不会涉及到这种类型的注解,这个注解必须定义在类、接口或者枚举类级别。实用的Listener包括ISuiteListener、ITestListener和IInvokedMethodListener,他们可以在suite级别、test级别和test method一些执行点执行一些自定义操作,如打印日志。
三、testng.xml 文件
TestNG 与 Junit 比较大的一个差异就是前者通过 testng.xml 文件来配置测试用例的执行。testng.xml 文件可以很好的控制要执行的测试用例的粒度,即测试包,测试类,测试方法。
- TestNG 官方文档:http://testng.org/doc/documentation-main.html
testng.xml
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="Suite1" verbose="1" ><test name="Nopackage" ><classes><class name="NoPackageTest" /></classes></test><test name="Regression1"><classes><class name="test.sample.ParameterSample"/><class name="test.sample.ParameterTest"/></classes></test>
</suite>
你可以指定包名称代替类名:
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="Suite1" verbose="1" ><test name="Regression1" ><packages><package name="test.sample" /></packages></test>
</suite>
在本例中,TestNG将查看test.sample包中的所有类,并且只保留有TestNG注释的类。
你还可以指定包含和排除的组和方法:
<test name="Regression1"><groups><run><exclude name="brokenTests" /> //排除不执行的测试用例<include name="checkinTests" /> //指定执行的测试用例</run></groups> <classes><class name="test.IndividualMethodsTest"><methods><include name="testMethod" /></methods></class></classes>
</test>
你还可以在 testng.xml 中定义新的组。并在属性中指定额外的细节,比如是否并行运行测试、使用多少线程、是否运行JUnit测试等等……
在默认情况下,TestNG将按照在XML文件中找到的顺序运行测试。如果您希望在这个文件中列出的类和方法以不可预测的顺序运行,则将preserve-order属性设置为false。
<test name="Regression1" preserve-order="false"><classes><class name="test.Test1"><methods><include name="m3" /><include name="m1" /><include name="m2" /></methods></class><class name="test.Test2" /></classes>
</test>
preserve-order 参数用于控制测试用例的执行顺序。
- 如果为:Ture,测试用例的顺序为:m3 > m1 > m2;
- 如果为 :false,那么默认会按照用例的名称的有字母/数字的顺序执行:m1 > m2 > m3;
多线程执行测试用例:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"><suite name="suite1" parallel="classes" thread-count="2" ><test verbose="2" name="test1"><classes><class name="com.testng.test.testBaidu"/><class name="com.testng.test.testYoudao"/></classes></test>
</suite>
- parallel 表示你需要对哪个级别进行多线程;
- thread-count 用于指定线程的个数;
参考:
parallel=“methods” 每个方法都将采用独立的线程进行测试;
parallel=“classes” 将把每个<class>
标签内的所有方法在同一个线程中执行,但是<classes>
中的不同<class>
将会以不同的线程执行;
parallel=“instances” TestNg 将把同一个实例内的所有方法运行在同一个线程中,但是如果两个方法是在不同的实例中,那么他们将会在不同的线程中执行;
四、运行测试用例、查看测试结果
在运行测试用例之前,我们首先要用 TestNG 编写一个单元测试用例,我们以百度搜索为例,代码如下:
testBaidu.java
package com.testng.test;
import ......public class testBaidu {private WebDriver driver;private String baseUrl;@BeforeClasspublic void setUp() throws Exception {driver = new ChromeDriver();baseUrl = "https://www.baidu.com/";driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);}@Testpublic void testCase() throws Exception {driver.get(baseUrl + "/");driver.findElement(By.id("kw")).sendKeys("testNG");driver.findElement(By.id("su")).click();Thread.sleep(2000);String title =driver.getTitle();assertEquals(title,"testNG_百度搜索");}@AfterClasspublic void tearDown() throws Exception {driver.quit();}
}
TestNG 的运行测试用例有3种方法:
(1)方法一:通过class来执行:
org.testng.TestNG testng = new org.testng.TestNG();
testng.setTestClasses(new Class[]{TestCaseMonitor.class});
testng.run();
新建一个TestNG对象,setTestClass传递一个用例的class进去,然后执行用例run();
(1)方法二
在测试用例文件右键,选择 Run As —> TestNG Test 运行;
注意:若直接在绿色框里的文件名处右击“run as ”无选项,因为没有主函数;需要在红色框 testCase() 处右击运行;
(2)方法三
创建 testng.xml 配置文件;其实方法一也需要 xml 配置文件,只是 TestNG 默认帮我们创建了一个 testng-customsuite.xml,该文件的路径在上面的运行结果中;
在包中右键,选择创建 file;输入文件名为 testng.xml,默认一般就叫这名;点击“Finish”完成文件的创建;
testng.xml 配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"><suite name="suite1"><test verbose="2" name="test1"><classes><class name="com.testng.test.testBaidu"/></classes></test>
</suite>
在 testng.xml 文件中右键 Run As —> TestNG Test 运行;
TestNG 默认会在项目的目录下生成测试报告: .../WorkSpace\myPro\test-output/emailable-report.html
五、TestNG 测试技巧
1、依赖性测试
JUnit 框架想达到的一个目标就是测试隔离。
它的缺点是:人们很难确定测试用例执行的顺序,而这对于任何类型的依赖性测试都非常重要。开发者们使用了多种技术来解决这个问题,例如,按字母顺序指定测试用例,或是更多地依靠 fixture 来适当地解决问题。如果测试成功,这些解决方法都没什么问题。但是,如果测试不成功,就会产生一个很麻烦的后果: 所有后续的依赖测试也会失败。在某些情况下,这会使大型测试套件报告出许多不必要的错误。
例如:假设有一个测试套件测试一个需要登录的 Web 应用程序。您可以创建一个有依赖关系的方法,通过登录到这个应用程序来创建整个测试套件,从而避免 JUnit 的隔离机制。这种解决方法不错,但是如果登录失败, 即使登录该应用程序后的其他功能都正常工作,整个测试套件依然会全部失败!
跳过,而不是标为失败!
与 JUnit 不同,TestNG 利用 Test 注释的 dependsOnMethods 属性来应对测试的依赖性问题。有了这个便利的特性,就可以轻松指定依赖方法。例如,前面所说的登录将在某个方法之前运行。此外,如果依赖 方法失败,它将被跳过,而不是标记为失败。
这个特性就非常有用了,例 126 邮箱的测试,用例都基于登录邮箱之后的验证,如果验证登录的用例失败了,那么登录邮箱收发邮件的用例其实也没必要运行了。
testMail.java
package com.testng.test;
import ......public class testMail {public static void login(WebDriver driver,String username,String password){ //登录方法driver.findElement(By.id("idInput")).clear();driver.findElement(By.id("idInput")).sendKeys(username);driver.findElement(By.id("pwdInput")).clear();driver.findElement(By.id("pwdInput")).sendKeys(password);driver.findElement(By.id("loginBtn")).click();}public static void logout(WebDriver driver){ //退出方法driver.findElement(By.linkText("退出")).click();}@Testpublic void verifyLogin() { //验证登录测试用例WebDriver driver = new ChromeDriver();driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);driver.get("http://www.126.com");String username = "testingwtb";String password = "a123456";login(driver,username,password); //调用登录方法String text = driver.findElement(By.id("spnUid")).getText();assertEquals(text,"testingwtb@126.com");logout(driver); //调用退出方法driver.quit();}@Test (dependsOnMethods = {"verifyLogin"}) //这个测试用例是否执行取决于verifyLogin方法public void verifySearchMail() {WebDriver driver = new ChromeDriver();driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);driver.get("http://www.126.com");String username = "testingwtb";String password = "a123456";login(driver,username,password);//搜索WebElement search = driver.findElement(By.xpath("//input[@class='nui-ipt-input' and @type='text']"));search.sendKeys("小明");search.sendKeys(Keys.ENTER);String text = driver.findElement(By.className("nui-title-text")).getText();System.out.println(text);assertEquals(text,"搜索结果");logout(driver);driver.quit();}
}
上面的例子中定义了两个测试:一个验证登录,另一个验证账户信息。请注意,通过使用 Test 注释的dependsOnMethods={“verifyLogin”}子句,verifyAccountInfo 测试指定了它依赖 verifyLogin()方法。
2、忽略测试
有时候测试用例还没准备好,可以给测试用例加上**@Test(enable = false)**,用来禁止此测试用例;
testMail.java
package com.testng.test;
import ......public class testMail {public static void login(WebDriver driver,String username,String password){ //登录方法driver.findElement(By.id("idInput")).clear();driver.findElement(By.id("idInput")).sendKeys(username);driver.findElement(By.id("pwdInput")).clear();driver.findElement(By.id("pwdInput")).sendKeys(password);driver.findElement(By.id("loginBtn")).click();}public static void logout(WebDriver driver){ //退出方法driver.findElement(By.linkText("退出")).click();}@Test(enabled = false) //忽略这条用例 public void verifyLogin() { WebDriver driver = new ChromeDriver();driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);driver.get("http://www.126.com");String username = "testingwtb";String password = "a123456";login(driver,username,password);String text = driver.findElement(By.id("spnUid")).getText();assertEquals(text,"testingwtb@126.com");logout(driver);driver.quit();}
}
3、参数化测试
TestNG 中另一个有趣的特性是参数化测试。
在 JUnit 中,如果您想改变某个受测方法的参数组,就只能给每个不同的参数组编写一个测试用例。多数情况下,这不会带来太多麻烦。然而,我们有时会碰到一些情况,对其中的业务逻辑,需要运行的测试数目变化范围很大。在这样的情况下,使用 JUnit 的测试人员往往会转而使用 FIT(验收测试框架)这样的框架,因为这样就可以用表格数据驱动测试。但是 TestNG 提供了开箱即用的类似特性。
通过在 TestNG 的 XML 配置文件中放入参数化数据,就可以对不同的数据集重用同一个测试用例,甚至有可能会得到不同的结果。这种技术完美地避免了只能假定一切正常的测试,或是没有对边界进行有效验证的情况。
Eg1 : 通过 testng.xml 传递参数给测试代码
testBaidu.java
package com.testng.test;
import ......public class testBaidu {private WebDriver driver;private String baseUrl;@BeforeClasspublic void setUp() throws Exception {driver = new ChromeDriver();baseUrl = "https://www.baidu.com/";driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);}@Test@Parameters("test1") //设置测试方法的入参public void testCase(String test1) throws Exception {driver.get(baseUrl + "/");System.out.println("sreach key value:"+ test1);driver.findElement(By.id("kw")).sendKeys(test1);driver.findElement(By.id("su")).click();Thread.sleep(2000);String title =driver.getTitle();assertEquals(title,test1+"_百度搜索");}@AfterClasspublic void tearDown() throws Exception {driver.quit();}
}
对应的 testng.xml 文件配置如下:
testng.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="suite1"><parameter name="test1" value="testng" /> //定义 test1 的 value <test name="test1"><classes><class name="com.testng.test.testBaidu"/></classes></test>
</suite>
Eg2 : 通过 DataProvider 传递参数
testng.xml
package com.testng.test;
import ......public class mailLogin {private WebDriver driver;private String baseUrl;@BeforeClasspublic void setUp() throws Exception {driver = new ChromeDriver();baseUrl = "http://www.126.com";driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);}@DataProvider(name="user") //定义对象数组public Object[][] Users(){return new Object[][]{{"testingwtb","a123456"},};}@Test(dataProvider="user") //调用 user 数组的值public void testCase(String username,String password) throws Exception {driver.get(baseUrl);driver.findElement(By.id("idInput")).clear();driver.findElement(By.id("idInput")).sendKeys(username);driver.findElement(By.id("pwdInput")).clear();driver.findElement(By.id("pwdInput")).sendKeys(password);driver.findElement(By.id("loginBtn")).click();Thread.sleep(2000);String text = driver.findElement(By.id("spnUid")).getText();assertEquals(text,username+"@126.com");}@AfterClasspublic void tearDown() throws Exception {driver.quit();}
}