1. 程式人生 > >【TestNG】TestNG併發執行用例詳解和範例

【TestNG】TestNG併發執行用例詳解和範例

前言

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 ([email protected])
...

[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 ([email protected])
...

[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 ([email protected])
...

[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級併發

此併發方法與前面幾種併發方法類似,只是需要修改parallelinstances即可,為一個例項一個併發,範例如下:

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 ([email protected])
...

[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
[Tes