開始,我們先來思考一個場景,有一個測試類需要根據測試傳入的N個參數進行N次執行。
方式一,可以直接在suite.xml中設置多個class,輸入不同的參數執行測試用例
xml配置:
<suite name="Factory Test" verbose="1">
<test name="Regression1">
<parameter name="key" value="1"/>
<classes>
<class name="com.local.testng.SimpleTest">
</class>
</classes>
</test>
<test name="Regression2">
<parameter name="key" value="2"/>
<classes>
<class name="com.local.testng.SimpleTest">
</class>
</classes>
</test>
</suite>
運行兩次測試用例,第一次傳key=“1”
,第二次傳key=2
。
測試用例:
public class SimpleTest {
private static Logger logger = null;
@BeforeTest
public void setUp() {
logger = Logger.getLogger(WebTest.class);
}
@Parameters({"key"})
@Test
public void testServer(String key) {
logger.info(">>> "+key);
}
}
執行結果:
[WebTest] [INFO] >>> 1
[WebTest] [INFO] >>> 2
===============================================
Factory Test
Total tests run: 2, Failures: 0, Skips: 0
===============================================
從執行結果上看,測試用例確實使用了不同參數運行了兩次,但是這裏有明顯的缺點:
- xml配置參數只能是String類型
- 需要配置多
<test>
,非常冗餘和不好管理
方式二,使用
@Factory
來配置工廠類來動態傳入不同參數運行同一個測試用例
預執行的Test:
public class WebTest {
private int m_numberOfTimes;
private static Logger logger = null;
public WebTest(int numberOfTimes) {
m_numberOfTimes = numberOfTimes;
}
@BeforeTest
public void setUp() {
logger = Logger.getLogger(WebTest.class);
}
@Test
public void testServer() {
logger.info(">>> "+m_numberOfTimes);
}
}
需要多次執行testServer,輸出從0~2的值,既m_numberOfTimes
依次取值0,1,2輸出,我們使用Factory
來調用這個測試類:
public class WebTestFactory {
@Factory
public Object[] createInstance() {
Object[] result = new Object[3];
for (int i = 0; i < 3; i++) {
result[i] = new WebTest(i);
}
return result;
}
}
上面用Factory
註釋createInstance,返回Object
類型數組,循環生成了三個WebTest
的對象賦值其中,然後在xml
中調用這個class類:
<suite name="Factory Test" verbose="1">
<test name="Regression1">
<!--<parameter name="key" value="1"/>-->
<classes>
<class name="com.local.testng.WebTestFactory">
</class>
</classes>
</test>
</suite>
結果輸出:
[WebTest] [INFO] >>> 2
[WebTest] [INFO] >>> 1
[WebTest] [INFO] >>> 0
===============================================
Factory Test
Total tests run: 3, Failures: 0, Skips: 0
===============================================
看到這裏,仔細的同學可能會想,你這createInstance
什麼鬼,循環三次還不是寫死,哪來的動態?確實,我們來稍微修改下:
public class WebTestFactory {
@Parameters({"key"})
@Factory
public Object[] createInstance(String value) {
Integer num = Integer.valueOf(value);
Object[] result = new Object[num];
for (int i = 0; i < num; i++) {
result[i] = new WebTest(i);
}
return result;
}
}
修改完後,取xml
中配置參數key的值傳入createInstance
,比如現在我們想輸出0~4,只要改下配置文件參數值即可:
<suite name="Factory Test" verbose="1">
<test name="Regression1">
<parameter name="key" value="5"/>
<classes>
<class name="com.local.testng.WebTestFactory">
</class>
</classes>
</test>
</suite>
輸出結果:
[WebTest] [INFO] >>> 3
[WebTest] [INFO] >>> 2
[WebTest] [INFO] >>> 0
[WebTest] [INFO] >>> 4
[WebTest] [INFO] >>> 1
===============================================
Factory Test
Total tests run: 5, Failures: 0, Skips: 0
===============================================
可能到這裏,還是有同學會說,我的業務數據很複雜,可不是什麼123之類,可能就是數據庫裏查出來的一推業務數據。沒問題,接着我們來看看當Factory
和DataProvider
結合在一起,會產生什麼效果。
先看一個DataProvider:
public class WebTestDataProvider {
@DataProvider(name = "provider1")
public static Object[][] provider1() {
Object[][] provider = new Object[1][3];
HashMap<String, String> one = new HashMap<String, String> () {
{
put("one", "1");
}
};
HashMap<String, String> two = new HashMap<String, String> () {
{
put("two", "2");
}
};
return new Object[][] {
{1, "one", one},
{2, "two", two}
};
}
}
這裏順道講下DataProvider
的使用,分兩種:
- 直接在測試類裏面定義DataProvider,只能用於當前測試類
- 定義在一個專門的Class中,被其他測試類引用,需要注意的是如果是單獨定義一個Class來放置DataProvider,對應的DataProvider方法需要定義爲static,引用的時候還要指明具體類,這裏我們就使用這第二種方式。
定義Factory的類中的引用DataProvider
public class WebTestFactory {
@Factory(dataProvider = "provider1", dataProviderClass = com.local.testng.WebTestDataProvider.class)
public Object[] createInstance(Integer index, String name, HashMap<String, String> content) {
return new Object[] {new FactoryAndProviderTest(index, name, content)};
}
}
這裏注意一點
,createInstance的傳參需要合DataProvider返回的二維數據的每一行的數據一致,即個數、順序、類型都要一致,否則testng無法解析。
實際執行的測試類:
public class FactoryAndProviderTest {
private Integer index;
private String name;
private HashMap<String, String> content;
private static Logger logger = null;
public FactoryAndProviderTest(Integer index, String name, HashMap<String, String> content) {
this.index = index;
this.name = name;
this.content = content;
}
@BeforeTest
public void setUp() {
logger = Logger.getLogger(FactoryAndProviderTest.class);
}
@Test
public void testServer() {
logger.info("testServer>>> "+this.index);
logger.info("testServer>>> "+this.name);
logger.info("testServer>>> "+this.content.toString());
}
@Test
public void testServer1() {
logger.info("testServer1>>> "+this.index);
logger.info("testServer1>>> "+this.name);
logger.info("testServer1>>> "+this.content.toString());
}
}
suite配置:
<suite name="Factory Test" verbose="1">
<test name="Regression1">
<parameter name="key" value="5"/>
<classes>
<class name="com.local.testng.WebTestFactory">
</class>
</classes>
</test>
</suite>
運行後,FactoryAndProviderTest中的所有測試類都會循環使用DataProvider中的數據,執行程序的邏輯。
測試結果:
[FactoryAndProviderTest] [INFO] testServer>>> 2
[FactoryAndProviderTest] [INFO] testServer>>> two
[FactoryAndProviderTest] [INFO] testServer>>> {two=2}
[FactoryAndProviderTest] [INFO] testServer>>> 1
[FactoryAndProviderTest] [INFO] testServer>>> one
[FactoryAndProviderTest] [INFO] testServer>>> {one=1}
[FactoryAndProviderTest] [INFO] testServer1>>> 2
[FactoryAndProviderTest] [INFO] testServer1>>> two
[FactoryAndProviderTest] [INFO] testServer1>>> {two=2}
[FactoryAndProviderTest] [INFO] testServer1>>> 1
[FactoryAndProviderTest] [INFO] testServer1>>> one
[FactoryAndProviderTest] [INFO] testServer1>>> {one=1}
===============================================
Factory Test
Total tests run: 4, Failures: 0, Skips: 0
===============================================
講到這裏,大家可能想到當@DataProvider註釋某一個TestCase的時候,測試用例也會使用所有數據跑一遍程序邏輯,然後比較下當@Factory和@DataProvider結合,它可以讓某個測試類中的所有方法都遍歷DataProvider中的數據執行一遍程序邏輯,可以更方便的規劃測試用例地執行。