簡介
TestNG是Test Next Generation的縮寫,它的靈感來自於JUnit和NUnit,在它們基礎上增加了很多很牛的功能,比如說:
- 註解。
- 多執行緒,比如所有方法都在各自執行緒中,一個測試類一個執行緒等。
- 驗證程式碼是否多執行緒安全。
- 靈活的測試配置。
- 支援資料驅動(@DataProvider)。
- 支援引數化。
- 強大的執行機制(不需要TestSuite)。
- 能跟各種工具結合(比如IDEA、Maven等)。
- 內嵌BeanShell。
- 提供執行時和日誌的JDK函式(不需要新增dependencies)。
- 提供應用伺服器測試依賴的方法。
一句話總結就是,TestNG強大到能覆蓋所有測試型別:單元測試、功能測試、端到端測試、整合測試等等等。
一個簡單的示例如下:
package example1;
import org.testng.annotations.*;
public class SimpleTest {
@BeforeClass
public void setUp() {
// code that will be invoked when this test is instantiated
}
@Test(groups = { "fast" })
public void aFastTest() {
System.out.println("Fast test");
}
@Test(groups = { "slow" })
public void aSlowTest() {
System.out.println("Slow test");
}
}
- setUp()會在測試類建立後,測試方法執行前執行。
- 測試方法名字可以任意的,TestNG通過@Test註解來標識。
- 可以把測試方法按group分組。
然後使用xml來配置執行策略:
<project default="test">
<path id="cp">
<pathelement location="lib/testng-testng-5.13.1.jar"/>
<pathelement location="build"/>
</path>
<taskdef name="testng" classpathref="cp"
classname="org.testng.TestNGAntTask" />
<target name="test">
<testng classpathref="cp" groups="fast">
<classfileset dir="build" includes="example1/*.class"/>
</testng>
</target>
</project>
使用ant呼叫(下面第2節給出了最新的執行方式):
c:> ant
Buildfile: build.xml
test:
[testng] Fast test
[testng] ===============================================
[testng] Suite for Command line test
[testng] Total tests run: 1, Failures: 0, Skips: 0
[testng] ===============================================
BUILD SUCCESSFUL
Total time: 4 seconds
最後就能看報告了:
start test-output\index.html (on Windows)
如何執行TestNG
除了前面提到的ant呼叫,更常見的執行TestNG的方式是以下兩種:
- IDEA
- Maven
IDEA
在IDEA中執行TestNG第①種方式是點選方法或類前面的小箭頭:
第②種方式是在方法或類內部點選右鍵:
第③種方式是使用testng.xml,IDEA可以右鍵testng.xml來執行,在檔名和檔案內部點選均可:
testng.xml支援很多種配置,比如配置測試類:
<!DOCTYPE suite SYSTEM "https://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 "https://testng.org/testng-1.0.dtd" >
<suite name="Suite1" verbose="1" >
<test name="Regression1" >
<packages>
<package name="test.sample" />
</packages>
</test>
</suite>
配置組和方法:
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd" >
<suite name="Suite1" verbose="1" >
<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>
</suite>
預設TestNG會按xml順序執行,可以設定 preserve-order
為false變成隨機順序:
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd" >
<suite name="Suite1" verbose="1" >
<test name="Regression1" preserve-order="false">
<classes>
<class name="test.Test1">
<methods>
<include name="m1" />
<include name="m2" />
</methods>
</class>
<class name="test.Test2" />
</classes>
</test>
</suite>
Maven
官方教程給出了命令列執行TestNG:
java org.testng.TestNG testng1.xml [testng2.xml testng3.xml ...]
實際呼叫是調不通的,可以藉助Maven來實現。
先在pom.xml新增:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
</plugins>
</build>
然後執行mvn clean test
命令就行啦。
測試方法和測試類
使用了@Test註解的方法就是測試方法,包含測試方法的類就是測試類。比如:
package example1;
import org.testng.annotations.*;
public class SimpleTest {
@BeforeClass
public void setUp() {
// code that will be invoked when this test is instantiated
}
@Test(groups = { "fast" })
public void aFastTest() {
System.out.println("Fast test");
}
@Test(groups = { "slow" })
public void aSlowTest() {
System.out.println("Slow test");
}
}
@Test也能放在測試類上面,這樣測試類下的所有方法都是測試方法,比如:
@Test
public class Test1 {
public void test1() {
}
public void test2() {
}
}
而且可以在測試類和測試方法上同時使用@Test,比如給某個Test加入分組:
@Test
public class Test1 {
public void test1() {
}
@Test(groups = "g1")
public void test2() {
}
}
有個點需要注意的是@Test註解的測試方法,預設是會忽略返回值的,除非在testng.xml配置:
<suite allow-return-values="true">
or
<test allow-return-values="true">
測試組
測試組其實就是給測試方法打標記,比如冒煙測試用例和功能測試用例:
public class Test1 {
@Test(groups = { "functest", "checkintest" })
public void testMethod1() {
}
@Test(groups = {"functest", "checkintest"} )
public void testMethod2() {
}
@Test(groups = { "functest" })
public void testMethod3() {
}
}
如果只配置functest,就會執行全部測試方法:
<test name="Test1">
<groups>
<run>
<include name="functest"/>
</run>
</groups>
<classes>
<class name="example1.Test1"/>
</classes>
</test>
如果只配置checkintest,就會只執行前面2個方法:
<test name="Test1">
<groups>
<run>
<include name="checkintest"/>
</run>
</groups>
<classes>
<class name="example1.Test1"/>
</classes>
</test>
除了指定完整name,也可以使用正規表示式:
@Test
public class Test1 {
@Test(groups = { "windows.checkintest" })
public void testWindowsOnly() {
}
@Test(groups = {"linux.checkintest"} )
public void testLinuxOnly() {
}
@Test(groups = { "windows.functest" )
public void testWindowsToo() {
}
}
<test name="Test1">
<groups>
<run>
<include name="windows.*"/>
</run>
</groups>
<classes>
<class name="example1.Test1"/>
</classes>
</test>
進一步,測試組除了include測試方法,還可以exclude:
<test name="Test1">
<classes>
<class name="example1.Test1">
<methods>
<include name=".*enabledTestMethod.*"/>
<exclude name=".*brokenTestMethod.*"/>
</methods>
</class>
</classes>
</test>
還有一個辦法是使用@Test和@Before/After的
enabled
屬性。
測試組也能進行巢狀:
<test name="Regression1">
<groups>
<define name="functest">
<include name="windows"/>
<include name="linux"/>
</define>
<define name="all">
<include name="functest"/>
<include name="checkintest"/>
</define>
<run>
<include name="all"/>
</run>
</groups>
<classes>
<class name="test.sample.Test1"/>
</classes>
</test>
<define></define>
定義了父分組,裡面<include>
的就是子分組。
同樣的,巢狀分組也可以對子分組進行exclude:
@Test(groups = {"checkintest", "broken"} )
public void testMethod2() {
}
<test name="Simple example">
<groups>
<run>
<include name="checkintest"/>
<exclude name="broken"/>
</run>
</groups>
<classes>
<class name="example1.Test1"/>
</classes>
</test>
最後,對於分組的位置,如果測試類和測試方法都標記了分組,那麼測試類的分組會作用到所有方法中,比如:
@Test(groups = {"checkin-test"})
public class All {
@Test(groups = {"func-test")
public void method1() { ...}
public void method2() { ...}
}
method2()屬於checkin-test分組,method1屬於func-test和checkin-test兩個分組。
TestNG引數化
TestNG引數化有兩種方式,第一種是從testng.xml讀取資料,第二種是通過程式碼讀取資料。
從testng.xml讀取資料
直接看示例:
@Parameters({ "first-name" })
@Test
public void testSingleString(String firstName) {
System.out.println("Invoked testString " + firstName);
assert "Cedric".equals(firstName);
}
<suite name="My suite">
<parameter name="first-name" value="Cedric"/>
<test name="Simple example">
<-- ... -->
-
@Parameters指定引數化名字。
-
測試方法入參與引數化名字一一對應。
-
testng.xml中
<parameter>
定義引數化的值。在testng.xml中,
<parameter>
既可以定義在<suite>
中也可以定義在<test>
中,如果有同名的,會以<test>
的覆蓋<suite>
。
@Parameters既可以作用到@Test
,也可以作用到 @Before/After
和@Factory
,比如:
@Parameters({ "datasource", "jdbcDriver" })
@BeforeMethod
public void beforeTest(String ds, String driver) {
m_dataSource = ...; // look up the value of datasource
m_jdbcDriver = driver;
}
也可以作用到測試類的構造方法中,但是隻能最多一個構造方法,這樣就能在初始化類的時候,進行引數化賦值,便於測試方法使用
@Optional用於標識引數是否可選,比如:
@Parameters("db")
@Test
public void testNonExistentParameter(@Optional("mysql") String db) { ... }
- 如果db這個引數取不到名字,那麼就會取mysql的值。
通過程式碼讀取資料
第一種引數化方式其實比較雞肋,第二種方式才是TestNG引數化的靈魂,用到了@DataProvider,它會返回一個二維陣列:
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class DPTest {
@DataProvider(name = "test1")
public Object[][] createData1() {
return new Object[][] {
{ "Cedric", 36},
{ "Anne", 37},
};
}
@Test(dataProvider = "test1")
public void verifyData1(String n1, Integer n2) {
System.out.println(n1 + " " + n2);
}
}
- @DataProvider用於生產資料,name是唯一標識。
- 在@Test中通過dataProvider屬性指定name。
- 測試方法的入參跟陣列中元素一一對應。
預設@DataProvider和@Test是在同一個類中,如果想放在不同的類,那麼需要定義為靜態方法(或者無引數構造方法的類),比如:
import org.testng.annotations.DataProvider;
public class StaticProvider {
@DataProvider(name = "create")
public static Object[][] createData() {
return new Object[][] {
new Object[] {42}
};
}
}
import org.testng.annotations.Test;
public class DiffClazzTest {
@Test(dataProvider = "create", dataProviderClass = StaticProvider.class)
public void test(Integer n) {
System.out.println(n);
}
}
- createData()為static。
- 需要額外通過@Test的dataProviderClass屬性指定@DataProvider所在的類。
@DataProvider的返回值(引數型別)除了已經提到的Object[][]
,還可以是Iterator<Object[]>
,它不會一次性生成所有資料,而是每呼叫一次生成一次,節約記憶體,比如:
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.util.Arrays;
import java.util.Iterator;
public class IterTest {
@DataProvider(name = "test1")
public Iterator<Object[]> createData1() {
Object[][] myObjects = new Object[][]{
{"Cedric", 36},
{"Anne", 37},
};
return Arrays.asList(myObjects).iterator();
}
@Test(dataProvider = "test1")
public void verifyData1(String n1, Integer n2) {
System.out.println(n1 + " " + n2);
}
}
看到這裡,對@DataProvider已經有了足夠的認識,它支援兩種引數型別:
Object[][]
Iterator<Object[]>
假如測試方法只有一個入參,是不是隻能用二維來實現:
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.util.Arrays;
import java.util.Iterator;
public class IterTest {
@DataProvider(name = "test1")
public Iterator<Object[]> createData1() {
Object[][] myObjects = new Object[][]{{"x"}, {"y"}};
return Arrays.asList(myObjects).iterator();
}
@Test(dataProvider = "test1")
public void verifyData1(String n) {
System.out.println(n);
}
}
其實不是,@DataProvider支援一維陣列:
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class IterTest {
@DataProvider(name = "test1")
public Object[] createData1() {
Object[] myObjects = new Object[]{"x", "y"};
return myObjects;
}
@Test(dataProvider = "test1")
public void verifyData1(String n) {
System.out.println(n);
}
}
以及一維陣列的迭代器:
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.util.Arrays;
import java.util.Iterator;
public class IterTest {
@DataProvider(name = "test1")
public Iterator<Object> createData1() {
Object[] myObjects = new Object[]{"x", "y"};
return Arrays.asList(myObjects).iterator();
}
@Test(dataProvider = "test1")
public void verifyData1(String n) {
System.out.println(n);
}
}
最精彩的來了,@DataProvider支援反射,也就是反向獲取測試方法的資訊:
@DataProvider(name = "dp")
public Object[][] createData(Method m) {
System.out.println(m.getName()); // print test method name
return new Object[][] { new Object[] { "Cedric" }};
}
@Test(dataProvider = "dp")
public void test1(String s) {
}
@Test(dataProvider = "dp")
public void test2(String s) {
}
- createData的入參是java.lang.reflect.Method,這樣就能獲取到測試方法的資訊,比如這裡的getName()會依次拿到test1、test2。
@DataProvider還支援併發:
@DataProvider(parallel = true)// ...
預設是10個執行緒,可以在testng.xml中修改:
<suite name="Suite1" data-provider-thread-count="20" >...
一個xml共享一個執行緒池,如果要用多個執行緒池,那麼需要建立多個testng.xml。
錦上添花的是,TestNG的引數化會列印在測試報告中:
指定用例執行順序
TestNG用例的執行順序有兩種方式來指定:註解和XML。
註解
使用@Test的dependsOnMethods屬性:
@Test
public void serverStartedOk() {}
@Test(dependsOnMethods = { "serverStartedOk" })
public void method1() {}
或者dependsOnGroups屬性:
@Test(groups = { "init" })
public void serverStartedOk() {}
@Test(groups = { "init" })
public void initEnvironment() {}
@Test(dependsOnGroups = { "init.*" })
public void method1() {}
@Before/After也能實現初始化,但是它們的結果不會出現在測試報告中。
預設TestNG會強制校驗,依賴的用例必須成功才會執行當前用例,否則當前用例會被標記為SKIP,這叫做強依賴。通過設定alwaysRun=true可以變成弱依賴,無論依賴用例執行成功與否,都會執行當前用例。
需要特別注意的是,依賴測試方法是按照測試類來進行執行的(group by class),比如b()方法依賴的a()方法有多個例項,那麼會按照以下順序執行:
a(1)
a(2)
b(2)
b(2)
舉個實際的例子,登入和登出,如果想達到以下效果:
signIn("us")
signOut("us")
signIn("uk")
signOut("uk")
那麼需要在XML中進行配置:
<suite name="Factory" group-by-instances="true">
or
<test name="Factory" group-by-instances="true">
XML
在testng.xml中使用<dependencies>
和depends-on
來指定用例順序:
<test name="My suite">
<groups>
<dependencies>
<group name="c" depends-on="a b" />
<group name="z" depends-on="c" />
</dependencies>
</groups>
</test>
動態建立測試用例
假設有這樣的測試用例,在測試時需要對網頁訪問多次,那麼在TestNG中會這樣編寫程式碼:
public class TestWebServer {
@Test(parameters = { "number-of-times" })
public void accessPage(int numberOfTimes) {
while (numberOfTimes-- > 0) {
// access the web page
}
}
}
<test name="T1">
<parameter name="number-of-times" value="10"/>
<classes>
<class name= "TestWebServer" />
</classes>
</test>
<test name="T2">
<parameter name="number-of-times" value="20"/>
<classes>
<class name= "TestWebServer"/>
</classes>
</test>
<test name="T3">
<parameter name="number-of-times" value="30"/>
<classes>
<class name= "TestWebServer"/>
</classes>
</test>
- 由於訪問次數不一,在testng.xml中定義了3個test,然後藉助引數化將訪問次數傳給@Test測試方法。
- 在@Test測試方法中迴圈遍歷numberOfTimes。
這種需求可以採用TestNG的Factory來對程式碼進行簡化:
import org.testng.annotations.Factory;
public class WebTestFactory {
@Factory
public Object[] createInstances() {
Object[] result = new Object[3];
for (int i = 0; i <= 2; i++) {
result[i] = new WebTest((i + 1) * 10);
}
return result;
}
}
import org.testng.annotations.Test;
public class WebTest {
private int m_numberOfTimes;
public WebTest(int numberOfTimes) {
m_numberOfTimes = numberOfTimes;
}
@Test
public void testServer() {
for (int i = 0; i < m_numberOfTimes; i++) {
// access the web page
System.out.println(i);
}
}
}
- WebTestFactory是工廠函式,返回Object[]。
- WebTestFactory動態建立了多個WebTest例項。
既可以在IDEA中點選WebTestFactory的執行按鈕執行測試:
注意如果執行WebTest,會提示No tests were found。
也可以在testng.xml中驅動:
<class name="WebTestFactory" />
還可以直接在程式碼中驅動:
TestNG testNG = new TestNG();
testNG.setTestClasses(WebTestFactory.class);
testNG.run();
@Factory和@Test一樣,都能使用dataProvider屬性,比如:
@Factory(dataProvider = "dp")
public FactoryDataProviderSampleTest(int n) {
super(n);
}
@DataProvider
static public Object[][] dp() {
return new Object[][] {
new Object[] { 41 },
new Object[] { 42 },
};
}
忽略部分測試用例
TestNG可以使用@Ignore註解忽略測試,比如:
import org.testng.annotations.Ignore;
import org.testng.annotations.Test;
@Ignore
public class TestcaseSample {
@Test
public void testMethod1() {
}
@Test
public void testMethod2() {
}
}
如果作用到測試類上,那麼它會忽略這個類下面的所有測試方法。如果只作用到測試方法上,那麼它就相當於@Test(enabled=false)。另外還能放在包上面:
@Ignore
package com.testng.master;
import org.testng.annotations.Ignore;
多執行緒並行測試用例
可以給@Test新增屬性,讓用例以多執行緒並行執行:
@Test(threadPoolSize = 3, invocationCount = 10, timeOut = 10000)
public void testServer() {
- threadPoolSize指3個執行緒。
- invocationCount指執行10次。
- timeOut指阻塞等待超時。
也可以在testng.xml中設定,thread-count指定執行緒數,parallel設定不同的值有不同的含義:
methods:
<suite name="My suite" parallel="methods" thread-count="5">
所有測試方法在不同的獨立執行緒中執行。
tests:
<suite name="My suite" parallel="tests" thread-count="5">
<test>
標籤內的測試方法會在同一個執行緒中執行,不同的<test>
標籤會在不同的獨立執行緒中執行。
classes:
<suite name="My suite" parallel="classes" thread-count="5">
同一個類中的測試方法會在同一個執行緒中執行,不同的類會在不同的獨立執行緒中執行。
instances:
<suite name="My suite" parallel="instances" thread-count="5">
同一個例項中的測試方法會在同一個執行緒中執行,不同的例項會在不同的獨立執行緒中執行。(Factory能建立多個例項)
重跑失敗用例
TestNG在執行後會把失敗的用例輸出到testng-failed.xml
檔案中,可以直接執行這個檔案來重跑失敗用例。
誇一句,這個設計真棒。
有時候,需要讓用例失敗時自動重試,那麼可以在程式碼中這樣實現:
import org.testng.IRetryAnalyzer;
import org.testng.ITestResult;
public class MyRetry implements IRetryAnalyzer {
private int retryCount = 0;
private static final int maxRetryCount = 3;
@Override
public boolean retry(ITestResult result) {
if (retryCount < maxRetryCount) {
retryCount++;
return true;
}
return false;
}
}
import org.testng.Assert;
import org.testng.annotations.Test;
public class TestclassSample {
@Test(retryAnalyzer = MyRetry.class)
public void test2() {
Assert.fail();
}
}
- 實現IRetryAnalyzer介面的retry方法。
- 在@Test的retryAnalyzer中指定重試類。
TestNG程式設計
除了IDEA和Maven這兩種執行方式,TestNG還可以直接在程式中呼叫執行:
TestListenerAdapter tla = new TestListenerAdapter();
TestNG testng = new TestNG();
testng.setTestClasses(new Class[] { Run2.class });
testng.addListener(tla);
testng.run();
- TestListenerAdapter是預設的,可以實現org.testng.ITestListener介面自定義TestListener。
- setTestClasses新增測試類。
- run()執行。
還可以通過程式設計建立一個虛擬的testng.xml,org.testng.xml包的XmlClass, XmlTest等提供了這個能力:
XmlSuite suite = new XmlSuite();
suite.setName("TmpSuite");
XmlTest test = new XmlTest(suite);
test.setName("TmpTest");
List<XmlClass> classes = new ArrayList<XmlClass>();
classes.add(new XmlClass("test.failures.Child"));
test.setXmlClasses(classes) ;
這段程式碼會建立一個這樣的testng.xml:
<suite name="TmpSuite" >
<test name="TmpTest" >
<classes>
<class name="test.failures.Child" />
<classes>
</test>
</suite>
XmlSuite可以通過TestNG程式呼叫:
List<XmlSuite> suites = new ArrayList<XmlSuite>();
suites.add(suite);
TestNG tng = new TestNG();
tng.setXmlSuites(suites);
tng.run();
XML中寫BeanShell
是的,TestNG的XML中可以寫BeanShell,用來替代<include>
和<exclude>
:
<test name="BeanShell test">
<method-selectors>
<method-selector>
<script language="beanshell"><![CDATA[
groups.containsKey("test1")
]]></script>
</method-selector>
</method-selectors>
<!-- ... -->
- CDATA用來防止跟XML標籤語法衝突。
- 預置了method(當前測試方法)、testngMethod(當前測試方法的描述)、groups(當前測試方法所屬分組)三個物件,可以用來做匹配。
- 使用
<script>
後,<include>
和<exclude>
會失效。
Listener
TestNG提供了很多Listener用來自定義TestNG行為,類似於Hook那個意思:
- IAnnotationTransformer
- IAnnotationTransformer2
- IHookable
- IInvokedMethodListener
- IMethodInterceptor
- IReporter
- ISuiteListener
- ITestListener
- ITestNGListener
- IAlterSuiteListener
比如用IAnnotationTransformer動態設定@Test屬性:
public class MyTransformer implements IAnnotationTransformer {
public void transform(ITest annotation, Class testClass,
Constructor testConstructor, Method testMethod)
{
if ("invoke".equals(testMethod.getName())) {
annotation.setInvocationCount(5);
}
}
}
如果想修改@Factory或@DataProvider,得用IAnnotationTransformer2。
比如用IMethodInterceptor設定用例組的執行順序:
public List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context) {
List<IMethodInstance> result = new ArrayList<IMethodInstance>();
for (IMethodInstance m : methods) {
Test test = m.getMethod().getConstructorOrMethod().getAnnotation(Test.class);
Set<String> groups = new HashSet<String>();
for (String group : test.groups()) {
groups.add(group);
}
//讓fast分組第一個執行
if (groups.contains("fast")) {
result.add(0, m);
}
else {
result.add(m);
}
}
return result;
}
比如想自定義測試開始前和測試後的行為,可以先寫個Listener:
public class BeforeAfterLog extends TestListenerAdapter {
public void onTestSuccess(ITestResult result) {
System.out.println("onTestSuccess");
}
public void onTestFailure(ITestResult result) {
System.out.println("onTestFailure");
}
}
然後在測試類上新增:
@Listeners(BeforeAfterLog.class)
public class MyTest {
}
這樣就不用每個測試類都寫@BeforeClass和@AfterClass了。
依賴注入
TestNG支援在測試方法中新增特定類來進行依賴注入(就是獲取TestNG相關資訊):
- ITestContext
- XmlTest 當前
<test>
標籤 - Method 當前呼叫的測試方法
- Object[] 當前測試方法的入參
- ITestResult 當前測試結果
不同註解支援的方式如下表所示:
示例:
public class NoInjectionTest {
@DataProvider(name = "provider")
public Object[][] provide() throws Exception {
return new Object[][] { { CC.class.getMethod("f") } };
}
@Test(dataProvider = "provider")
public void withoutInjection(@NoInjection Method m) {
Assert.assertEquals(m.getName(), "f");
}
@Test(dataProvider = "provider")
public void withInjection(Method m) {
Assert.assertEquals(m.getName(), "withInjection");
}
}
@NoInjection用於禁止依賴注入。
斷言
TestNG支援Java的assert關鍵字斷言,示例:
@Test
public void verifyLastName() {
assert "Beust".equals(m_lastName) : "Expected name Beust, for" + m_lastName;
}
也可以用JUnit斷言方法:
import static org.testng.AssertJUnit.*;
//...
@Test
public void verify() {
assertEquals("Beust", m_lastName);
}
日誌
用Listener來實現,直接看程式碼:
public class DotTestListener extends TestListenerAdapter {
private int m_count = 0;
@Override
public void onTestFailure(ITestResult tr) {
log("F");
}
@Override
public void onTestSkipped(ITestResult tr) {
log("S");
}
@Override
public void onTestSuccess(ITestResult tr) {
log(".");
}
private void log(String string) {
System.out.print(string);
if (++m_count % 40 == 0) {
System.out.println("");
}
}
}
測試報告
TestNG自帶了一個測試報告,執行完後會生成index.html,開啟就是,比較醜,推薦用Allure。
如果想獲取測試報告的資料,那麼可以從org.testng.IReporter介面的方法:
public void generateReport(List<ISuite> suites, String outputDirectory)
根據它的入參去拿。
如果想給測試報告新增資料,那麼可以使用org.testng.Reporter類:
Reporter.log("M3 WAS CALLED");
YAML替代XML
如果你煩透了XML,那麼可以試試YAML。
比如XML:
<suite name="SingleSuite" verbose="2" thread-count="4">
<parameter name="n" value="42" />
<test name="Regression2">
<groups>
<run>
<exclude name="broken" />
</run>
</groups>
<classes>
<class name="test.listeners.ResultEndMillisTest" />
</classes>
</test>
</suite>
換成YAML:
name: SingleSuite
threadCount: 4
parameters: { n: 42 }
tests:
- name: Regression2
parameters: { count: 10 }
excludedGroups: [ broken ]
classes:
- test.listeners.ResultEndMillisTest
舒服多了。不過TestNG本身沒有引入YAML依賴包,需要自己新增:
<dependency>
<groupid>org.yaml</groupid>
<artifactid>snakeyaml</artifactid>
<version>1.23</version>
</dependency>
彩蛋
看完了官方教程,感覺測試框架的功能基本上大同小異,只是技術實現上有所區別。讓我驚喜的是,TestNG執行失敗的用例可以自動生成一份失敗用例的xml檔案,直接拿來重跑就可以了。還有一直以為TestNG只能用XML,沒想到也能用YAML了。最後,它給出了-Dtestng.mode.dryrun=true
這個引數,猜猜它是幹嘛的。有時候只想看跑起來會執行哪些測試,而不想真正執行,那麼就可以用這個引數。
參考資料: