前言
TestNG有多種並發方式支持,方法的並發,class級的並發,test級的並發等;
根據實際應用可以靈活的配置和使用,下面分別對幾種並發方法進行說明:
一、方法級並發
方法級並發即method級並發,此種並發方式需要將xml中的suite
標簽的parallel
屬性設置為methods
並添加屬性thread-count
並設置其值,其會將所有的方法按照設定的並發數進行並發,譬如總共有4個測試用例,並發數設置為3,則會開三個線程,那么必然會有兩個用例是在同一個線程內的,跟用例在哪個class內沒關系,范例如下:
測試用例類一ThreadTest.java
:
package com.demo.test.testng; import org.testng.annotations.Test; public class ThreadTest { @Test() public void test1() { long id = Thread.currentThread().getId(); System.out.println("test1-1 thread id:"+id); } @Test public void test2() { long id = Thread.currentThread().getId(); System.out.println("test1-2 thread id:"+id); } }
測試用例類二ThreadTest2
:
package com.demo.test.testng; import org.testng.annotations.Test; public class ThreadTest2 { @Test() public void test1() { long id = Thread.currentThread().getId(); System.out.println("test2-1 thread id:"+id); } @Test public void test2() { long id = Thread.currentThread().getId(); System.out.println("test2-2 thread id:"+id); } }
xml設置並發數為3
,並發類型為methods
:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <suite name="threadSuite" parallel="methods" thread-count="3"> <test name="Test"> <classes> <class name="com.demo.test.testng.ThreadTest"/> <class name="com.demo.test.testng.ThreadTest2"/> </classes> </test> <!-- Test --> </suite> <!-- Suite -->
執行結果如下:
[RemoteTestNG] detected TestNG version 6.10.0
[TestNG] Running:
D:\software\workspace\testng\src\main\java\com\demo\test\testCase\ThreadTestXml.xml
test1-1 thread id:12
test2-1 thread id:14
test1-2 thread id:13
test2-2 thread id:13
===============================================
threadSuite
Total tests run: 4, Failures: 0, Skips: 0
===============================================
如上圖所示,確實是開了三個線程,且有兩個相同線程號的用例並非同一個測試類;
二、class級並發
此並發方式需要將xml中的suite
標簽內的屬性parallel
屬性設置為classes
,且添加屬性thread-count
並設置其值即可實現class級別並發,其會一個class內的所有方法放在一個線程內,根據線程數設置和總的class數來分配線程,譬如如果設置線程數為3
,而class
數目為2
,則會開兩個線程來分別運行兩個class
,而如果設置線程數為3
,且class數目為4
則將會有兩個class
在一個線程內,如下為一個簡單的范例:
兩個用例類如下:ThreadTest.java
package com.demo.test.testng; import org.testng.annotations.Test; public class ThreadTest { @Test() public void test1() { long id = Thread.currentThread().getId(); System.out.println("test1-1 thread id:"+id); } @Test public void test2() { long id = Thread.currentThread().getId(); System.out.println("test1-2 thread id:"+id); } }
ThreadTest2.java
package com.demo.test.testng; import org.testng.annotations.Test; public class ThreadTest2 { @Test() public void test1() { long id = Thread.currentThread().getId(); System.out.println("test2-1 thread id:"+id); } @Test public void test2() { long id = Thread.currentThread().getId(); System.out.println("test2-2 thread id:"+id); } }
測試xml如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <suite name="threadSuite" parallel="classes" thread-count="3" verbose="2"> <test name="Test"> <classes> <class name="com.demo.test.testng.ThreadTest"/> <class name="com.demo.test.testng.ThreadTest2"/> </classes> </test> <!-- Test --> </suite> <!-- Suite -->
運行結果:
[RemoteTestNG] detected TestNG version 6.10.0
...
... TestNG 6.10 by Cédric Beust (cedric@beust.com)
...
[TestNG] Running:
D:\software\workspace\testng\src\main\java\com\demo\test\testCase\ThreadTestXml.xml
[TestRunner] Starting executor for test Test with time out:2147483647 milliseconds.
test2-1 thread id:13
test1-1 thread id:12
test2-2 thread id:13
test1-2 thread id:12
PASSED: test1
PASSED: test1
PASSED: test2
PASSED: test2
===============================================
Test
Tests run: 4, Failures: 0, Skips: 0
===============================================
可以看到兩個類分別開了一個線程,同一個類中的用例在同一個線程內,符合預期;
那么如果有一個suite下有多個test
,這個並發設置會否將所有的test都計算在呢?來試一下,新添加一個測試用例Depend1.java
:
package com.demo.test.testng; import org.testng.Assert; import org.testng.annotations.Test; public class DependTest1 { @Test(groups= {"dependGroup1"}) public void dependTest1() { System.out.println("dependTest1"); } @Test(groups= {"dependGroup1"}) public void dependTest2() { System.out.println("dependTest2"); } @Test(groups="dependGroup2") public void dependTest4() { System.out.println("dependTest4"); Assert.assertFalse(false); } }
修改xml為如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <suite name="threadSuite" parallel="classes" thread-count="2" verbose="2"> <test name="Test1"> <classes> <class name="com.demo.test.testng.ThreadTest"/> <class name="com.demo.test.testng.ThreadTest2"/> </classes> </test> <!-- Test --> <test name="test2"> <classes> <class name="com.demo.test.testng.DependTest1"> </class></classes> </test> </suite> <!-- Suite -->
再次運行結果如下:
[RemoteTestNG] detected TestNG version 6.10.0
...
... TestNG 6.10 by Cédric Beust (cedric@beust.com)
...
[TestNG] Running:
D:\software\workspace\testng\src\main\java\com\demo\test\testCase\ThreadTestXml.xml
[TestRunner] Starting executor for test Test1 with time out:2147483647 milliseconds.
test2-1 thread id:13
test1-1 thread id:12
test2-2 thread id:13
test1-2 thread id:12
PASSED: test1
PASSED: test1
PASSED: test2
PASSED: test2
===============================================
Test1
Tests run: 4, Failures: 0, Skips: 0
===============================================
[TestRunner] Starting executor for test test2 with time out:2147483647 milliseconds.
dependTest1
dependTest2
dependTest4
PASSED: dependTest1
PASSED: dependTest2
PASSED: dependTest4
===============================================
test2
Tests run: 3, Failures: 0, Skips: 0
===============================================
===============================================
threadSuite
Total tests run: 7, Failures: 0, Skips: 0
===============================================
可以看到是先運行test1
(內含兩個測試類),開了兩個線程,而后再運行test2
(內含一個測試類),開了一個線程,而這兩個test內的三個class是並沒有放在一起運行的;
故這種並發設置是根據每個test
標簽生效的;
三、test級並發
test級並發為將xml中每個test標簽下的用例放在一個線程內並根據並發數和tet數目開線程的並發方法,需要將xml中的suite
標簽內的屬性parallel
屬性設置為tests
,且添加屬性thread-count
並設置其值即可,譬如有一個suite
下有兩個test
且並發數設置為2
,那么就會開兩個線程,每個線程都包含一個test
,范例如下:ThreadTest.java
:
package com.demo.test.testng; import org.testng.annotations.Test; public class ThreadTest { @Test() public void test1() { long id = Thread.currentThread().getId(); System.out.println("test1-1 thread id:"+id); } @Test public void test2() { long id = Thread.currentThread().getId(); System.out.println("test1-2 thread id:"+id); } }
ThreadTest2.java
:
package com.demo.test.testng; import org.testng.annotations.Test; public class ThreadTest2 { @Test() public void test1() { long id = Thread.currentThread().getId(); System.out.println("test2-1 thread id:"+id); } @Test public void test2() { long id = Thread.currentThread().getId(); System.out.println("test2-2 thread id:"+id); } }
修改DependTest1.java
:
package com.demo.test.testng; import org.testng.Assert; import org.testng.annotations.Test; public class DependTest1 { @Test(groups= {"dependGroup1"}) public void dependTest1() { long id = Thread.currentThread().getId(); System.out.println("dependTest1 id:"+id); } @Test(groups= {"dependGroup1"}) public void dependTest2() { long id = Thread.currentThread().getId(); System.out.println("dependTest2 id:"+id); } @Test(groups="dependGroup2") public void dependTest4() { long id = Thread.currentThread().getId(); System.out.println("dependTest4 id:"+id); Assert.assertFalse(false); } }
xml設置如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <suite name="threadSuite" parallel="tests" thread-count="2" verbose="2"> <test name="Test1"> <classes> <class name="com.demo.test.testng.ThreadTest"/> <class name="com.demo.test.testng.ThreadTest2"/> </classes> </test> <!-- Test --> <test name="test2"> <classes> <class name="com.demo.test.testng.DependTest1"> </class></classes> </test> </suite> <!-- Suite -->
運行結果:
[RemoteTestNG] detected TestNG version 6.10.0
...
... TestNG 6.10 by Cédric Beust (cedric@beust.com)
...
[TestNG] Running:
D:\software\workspace\testng\src\main\java\com\demo\test\testCase\ThreadTestXml.xml
[ThreadUtil] Starting executor timeOut:2147483647ms workers:2 threadPoolSize:2
dependTest1 id:13
test1-1 thread id:12
test1-2 thread id:12
dependTest2 id:13
test2-1 thread id:12
dependTest4 id:13
test2-2 thread id:12
PASSED: test1
PASSED: test2
PASSED: test1
PASSED: test2
===============================================
Test1
Tests run: 4, Failures: 0, Skips: 0
===============================================
PASSED: dependTest1
PASSED: dependTest2
PASSED: dependTest4
===============================================
test2
Tests run: 3, Failures: 0, Skips: 0
===============================================
===============================================
threadSuite
Total tests run: 7, Failures: 0, Skips: 0
===============================================
可見兩個test是開了兩個線程運行的,且同一個test內的用例都在同一個線程內;
四、instances級並發
此並發方法與前面幾種並發方法類似,只是需要修改parallel
為instances
即可,為一個實例一個並發,范例如下:
package com.demo.test.testng; import java.util.ArrayList; import java.util.List; import org.testng.annotations.Factory; import org.testng.annotations.Test; public class FactoryTest { private String host; private int port; public FactoryTest(String host, int port) { this.host=host; this.port=port; } @Test public void login() { long id = Thread.currentThread().getId(); System.out.println("login, host:"+host+";port"+port+";id:"+id); } @Test(dependsOnMethods="login") public void logout() { long id = Thread.currentThread().getId(); System.out.println("logout, host:"+host+";port"+port+";id:"+id); } @Factory public static Object[] create() { List<FactoryTest> list = new ArrayList<FactoryTest>(); list.add(new FactoryTest("10.10.10.1", 8080)); list.add(new FactoryTest("10.10.10.2", 8080)); return list.toArray(); } }
xml如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <suite name="threadSuite" parallel="instances" thread-count="4" verbose="2"> <test name="Test1"> <classes> <class name="com.demo.test.testng.FactoryTest" /> </classes> </test> </suite> <!-- Suite -->
運行結果如下:
[RemoteTestNG] detected TestNG version 6.10.0
...
... TestNG 6.10 by Cédric Beust (cedric@beust.com)
...
[TestNG] Running:
D:\software\workspace\testng\src\main\java\com\demo\test\testCase\ThreadTestXml.xml
[TestRunner] Starting executor for test Test1 with time out:2147483647 milliseconds.
login, host:10.10.10.1;port8080;id:12
login, host:10.10.10.2;port8080;id:13
logout, host:10.10.10.1;port8080;id:14
logout, host:10.10.10.2;port8080;id:15
PASSED: login
PASSED: login
PASSED: logout
PASSED: logout
===============================================
Test1
Tests run: 4, Failures: 0, Skips: 0
===============================================
===============================================
threadSuite
Total tests run: 4, Failures: 0, Skips: 0
===============================================
如上log可知,兩個用例,兩組參數,共四條用例開了四個線程,每個用例都是一個實例;
如果是沒有@Factory
注解的普通用例,則沒效果。
五、測試用例級並發
此級並發可以直接在用例內設置,如下為一個范例:
package com.demo.test.testng; import org.testng.annotations.Test; public class ThreadTest { @Test(threadPoolSize = 3, invocationCount = 6, timeOut = 1000) public void test1() { long id = Thread.currentThread().getId(); System.out.println("test1-1 thread id:"+id); } @Test public void test2() { long id = Thread.currentThread().getId(); System.out.println("test1-2 thread id:"+id); } }
如上圖代碼所示,test1
設置線程數為3,調用次數為6,超時時間為1000ms,運行結果如下:
[RemoteTestNG] detected TestNG version 6.10.0
[TestNG] Running:
C:\Users\dufei\AppData\Local\Temp\testng-eclipse--1263110808\testng-customsuite.xml
[ThreadUtil] Starting executor timeOut:1000ms workers:6 threadPoolSize:3
test1-1 thread id:13
test1-1 thread id:14
test1-1 thread id:12
test1-1 thread id:14
test1-1 thread id:13
test1-1 thread id:12
test1-2 thread id:1
PASSED: test1
PASSED: test1
PASSED: test1
PASSED: test1
PASSED: test1
PASSED: test1
PASSED: test2
===============================================
Default test
Tests run: 7, Failures: 0, Skips: 0
===============================================
===============================================
Default suite
Total tests run: 7, Failures: 0, Skips: 0
===============================================
[TestNG] Time taken by org.uncommons.reportng.HTMLReporter@1d371b2d: 48 ms
如上所示,test1
為三個線程,調用六次,超時為1000ms,符合預期;