Java自動化測試框架-08 - TestNG之並行性和超時篇 (詳細教程)

1、並行性和超時

您能夠指示TestNG以各類方式在單獨的線程中運行測試。html

能夠經過在suite標籤中使用 parallel 屬性來讓測試方法運行在不一樣的線程中。這個屬性能夠帶有以下這樣的值:java

2、並行套件(suites)

若是您正在運行多個套件文件(例如「 java org.testng.TestNG testng1.xml testng2.xml」),而且但願每一個套件在單獨的線程中運行,則這個頗有用。您可使用如下命令行標誌來指定線程池的大小:git

java org.testng.TestNG -suitethreadpoolsize 3 testng1.xml testng2.xml testng3.xml

相應的ant任務名稱爲suitethreadpoolsizegithub

3、並行測試,類和方法

在並行於<suite>標記屬性能夠取下列值之一:shell

<suite name="My suite" parallel="methods" thread-count="5">
<suite name="My suite" parallel="tests" thread-count="5">
<suite name="My suite" parallel="classes" thread-count="5">
<suite name="My suite" parallel="instances" thread-count="5">

parallel =「 methods」:TestNG將在單獨的線程中運行全部測試方法。依賴方法也將在單獨的線程中運行,可是它們將遵循您指定的順序。
parallel =「 tests」:TestNG將在同一線程中的同一<test>標記中運行全部方法,可是每一個<test>標記將位於單獨的線程中。這樣,您就能夠將全部不是線程安全的類納入同一個<test>中,並確保它們都將在同一線程中運行,同時利用TestNG使用盡量多的線程來運行測試。
平行=「類」:TestNG的將運行在相同的線程相同的類的全部方法,但每一個類將在單獨的線程中運行。
parallel =「 instances」:TestNG將在同一線程中的同一實例中運行全部方法,可是在兩個不一樣實例中的兩個方法將在不一樣線程中運行。
此外,屬性 thread-count容許您指定應爲此執行分配多少個線程。
注意:@Test屬性timeOut在並行和非並行模式下都可工做。
您還能夠指定從不一樣的線程調用@Test方法。您可使用屬性threadPoolSize來實現如下結果:編程

@Test(threadPoolSize = 3, invocationCount = 10,  timeOut = 10000)
public void testServer() {

在此示例中,將從三個不一樣的線程調用函數testServer十次。此外,十秒的超時保證沒有任何線程將永遠在該線程上阻塞。安全

4、從新運行失敗的測試

每當套件中的測試失敗時,TestNG都會在輸出目錄中建立一個名爲testng-failed.xml的文件。此XML文件包含必要的信息,以僅從新運行失敗的這些方法,從而使您能夠快速重現失敗,而沒必要運行整個測試。所以,典型的會話以下所示:框架

java -classpath testng.jar;%CLASSPATH% org.testng.TestNG -d test-outputs testng.xml
java -classpath testng.jar;%CLASSPATH% org.testng.TestNG -d test-outputs test-outputs\testng-failed.xml

請注意,testng-failed.xml將包含全部必需的依賴方法,所以能夠確保您運行失敗的方法而不會出現任何SKIP失敗。ide

有時,您可能但願TestNG在測試失敗時自動重試。在這種狀況下,您可使用重試分析器。當您將重試分析器綁定到測試時,TestNG會自動調用重試分析器,以肯定TestNG是否能夠再次重試測試用例,以查看是否剛剛經過的測試如今經過。這是使用重試分析器的方法:函數

  1. 構建接口org.testng.IRetryAnalyzer的實現
  2. 將此實現綁定到@Test註釋,例如@Test(retryAnalyzer = LocalRetry.class)

如下是重試分析器的示例實現,該示例最多重試一次測試三次。

import org.testng.IRetryAnalyzer;
import org.testng.ITestResult;
 
/**
 * @author 北京-宏哥
 * 
 * Java自動化測試框架-08 - TestNG之 TestNG之並行性和超時篇
 *
 * 2019年11月7日
 */

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;
 
/**
 * @author 北京-宏哥
 * 
 * Java自動化測試框架-08 - TestNG之 TestNG之並行性和超時篇
 *
 * 2019年11月7日
 */

public class TestclassSample {
 
  @Test(retryAnalyzer = MyRetry.class)
  public void test2() {
    Assert.fail();
  }
}

5、JUnit測試

TestNG能夠運行JUnit 3和JUnit 4測試。全部你須要作的就是把JUnit的jar文件在類路徑中,在指定JUnit測試類,testng.classNames 屬性和設置testng.junit屬性設置爲true:

<test name="Test1" junit="true">
  <classes>
    <!-- ... -->

在這種狀況下,TestNG的行爲相似於JUnit,這取決於在類路徑上找到的JUnit版本:

  • JUnit 3:
    • 您的課程中全部以test *開頭的方法都將運行
    • 若是您的測試類上有一個方法setUp(),它將在每一個測試方法以前調用
    • 若是您的測試類上有一個方法tearDown(),它將在每一個測試方法以後被調用
    • 若是您的測試類包含方法suite(),則將調用此方法返回的全部測試
  • JUnit 4:
    • TestNG將使用org.junit.runner.JUnitCore運行程序運行測試

 6、以編程的方式運行testng

您能夠從本身的程序中輕鬆調用TestNG:

TestListenerAdapter tla = new TestListenerAdapter();
TestNG testng = new TestNG();
testng.setTestClasses(new Class[] { Run2.class });
testng.addListener(tla);
testng.run();

本示例建立一個TestNG對象並運行測試類Run2它還添加了一個TestListener您可使用適配器類org.testng.TestListenerAdapter本身實現org.testng.ITestListener此接口包含各類回調方法,可以讓您跟蹤測試的開始時間,成功時間,失敗時間等。

一樣,您能夠在testng.xml文件上調用TestNG,也能夠本身建立一個虛擬的testng.xml文件。爲此,您可使用發現包org.testng.xml的類: XmlClassXmlTest等。這些類中的每個都對應於它們的XML標記對應物。

例如,假設您要建立如下虛擬文件:

<suite name="TmpSuite" >
  <test name="TmpTest" >
    <classes>
      <class name="test.failures.Child"  />
    <classes>
    </test>
</suite>

您將使用如下代碼:

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) ;

而後,您能夠將此XmlSuite傳遞給TestNG:

List<XmlSuite> suites = new ArrayList<XmlSuite>();
suites.add(suite);
TestNG tng = new TestNG();
tng.setXmlSuites(suites);
tng.run();

有興趣的:請參閱JavaDocs瞭解整個API。

7、BeanShell和高級組選擇

若是testng.xml<include><exclude>標記不足以知足您的須要,則可使用BeanShell表達式來肯定是否應在測試運行中包括某種測試方法。您能夠在<test>標記下指定此表達式

<test name="BeanShell test">
   <method-selectors>
     <method-selector>
       <script language="beanshell"><![CDATA[
         groups.containsKey("test1")
       ]]></script>
     </method-selector>
   </method-selectors>
  <!-- ... -->

<SCRIPT>標記中發現的testng.xml,TestNG的將忽略隨後的<包括><排除>在當前組和方法的<試驗>標記:你的BeanShell的表達將是決定是否一個測試方法的惟一方式是否包含在內。

如下是有關BeanShell腳本的其餘信息:

它必須返回一個布爾值。除了此約束以外,還容許使用任何有效的BeanShell代碼(例如,您可能但願在工做日返回true,在週末返回false,這將容許您根據日期以不一樣的方式運行測試)
爲了方便起見,TestNG定義瞭如下變量:
一、java.lang.reflect.Method method:當前的測試方法。
二、org.testng.ITestNGMethod testngMethod:當前測試方法的描述。
三、java.util.Map <String,String> groups:當前測試方法所屬的組的映射。
您可能但願用CDATA聲明包圍表達式(如上所示),以免冗長的保留XML字符引用。

8、Annotation Transformers

TestNG容許您在運行時修改全部註釋的內容。若是源代碼中的註釋大多數時候都是正確的,則這特別有用,可是在某些狀況下,您想覆蓋它們的值。

爲了實現此目的,您須要使用註釋轉換器。

Annotation Transformer是一個實現如下接口的類:

public interface IAnnotationTransformer {
 
  /**
   * This method will be invoked by TestNG to give you a chance
   * to modify a TestNG annotation read from your test classes.
   * You can change the values you need by calling any of the
   * setters on the ITest interface.
   *
   * Note that only one of the three parameters testClass,
   * testConstructor and testMethod will be non-null.
   *
   * @param annotation The annotation that was read from your
   * test class.
   * @param testClass If the annotation was found on a class, this
   * parameter represents this class (null otherwise).
   * @param testConstructor If the annotation was found on a constructor,
   * this parameter represents this constructor (null otherwise).
   * @param testMethod If the annotation was found on a method,
   * this parameter represents this method (null otherwise).
   */
  public void transform(ITest annotation, Class testClass,
      Constructor testConstructor, Method testMethod);
}

像全部其餘TestNG偵聽器同樣,您能夠在命令行或使用ant來指定此類:

java org.testng.TestNG -listener MyTransformer testng.xml
 

或以編程方式:

TestNG tng = new TestNG();
tng.setAnnotationTransformer(new MyTransformer());
// ...

調用 方法transform()時,能夠在TestNG繼續進行以前,調用ITest測試參數上的任何設置方法來更改其值。

例如,這是您如何重寫屬性invocationCount的方法,但僅在其中一個測試類的測試方法invoke()上:

/**
 * @author 北京-宏哥
 * 
 * Java自動化測試框架-08 - TestNG之 TestNG之並行性和超時篇
 *
 * 2019年11月7日
 */
public class MyTransformer implements IAnnotationTransformer {
  public void transform(ITest annotation, Class testClass,
      Constructor testConstructor, Method testMethod)
  {
    if ("invoke".equals(testMethod.getName())) {
      annotation.setInvocationCount(5);
    }
  }
}

IAnnotationTransformer僅容許您修改@Test註釋。若是須要修改另外一個TestNG批註(配置批註@Factory@DataProvider),請使用IAnnotationTransformer2

9、方法攔截器

一旦TestNG 計算好了測試方法會以怎樣的順序調用,那麼這些方法就會分爲兩組:

1.按照順序運行的方法。這裏全部的方法都有相關的依賴,而且全部這些方法按照特定順序運行。

2.不定順序運行的方法。這裏的方法不屬於第一個類別。方法的運行順序是隨機的,下一個說不許是什麼(儘管如此,默認狀況下TestNG會嘗試經過類來組織方法)。

爲了可以讓你更好的控制第二種類別,TestNG定義以下接口:

/**
 * @author 北京-宏哥
 * 
 * Java自動化測試框架-08 - TestNG之 TestNG之並行性和超時篇
 *
 * 2019年11月7日
 */
public interface IMethodInterceptor {
   
  List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context);
 
}

方法中叫作methods的那個列表參數包含了全部以不定序運行的方法。你的 intercept 方法也要返回一個 IMethodInstance列表,它多是下面狀況之一:

1.內容與參數中接收的一致,可是順序不一樣

2.一組 IMethodInstance 對象

3.更大的一組 IMethodInstance對象

一旦你定義了攔截器,就把它傳遞個TestNG,用下面的方式:

java -classpath "testng-jdk15.jar:test/build" org.testng.TestNG -listener test.methodinterceptors.NullMethodInterceptor
   -testclass test.methodinterceptors.FooTest

有關等效的ant語法,請參見ant文檔中listeners屬性

例如,這是一個方法攔截器,它將對方法進行從新排序,以便始終首先運行屬於「快速」組的測試方法:

/**
 * @author 北京-宏哥
 * 
 * Java自動化測試框架-08 - TestNG之 TestNG之並行性和超時篇
 *
 * 2019年11月7日
 */
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);
    }
    if (groups.contains("fast")) {
      result.add(0, m);
    }
    else {
      result.add(m);
    }
  }
  return result;
}

 10、TestNG偵聽器

有幾個接口可以讓您修改TestNG的行爲。這些接口普遍地稱爲「 TestNG偵聽器」。如下是一些聽衆:

當實現這些接口之一時,能夠經過如下兩種方式之一讓TestNG知道它:

11、使用的testng.xml或Java的指定監聽器

這是在testng.xml文件中定義偵聽器的方法:

<suite>
 
  <listeners>
    <listener class-name="com.example.MyListener" />
    <listener class-name="com.example.MyMethodInterceptor" />
  </listeners>
 
...

或者,若是您更喜歡用Java定義這些偵聽器,則:

@Listeners({ com.example.MyListener.class, com.example.MyMethodInterceptor.class })
public class MyTest {
  // ...
}

@Listeners註釋能夠包含任何擴展類org.testng.ITestNGListener 除了 IAnnotationTransformerIAnnotationTransformer2緣由是這些偵聽器須要在過程的早期就知道,以便TestNG可使用它們來重寫您的註釋,所以您須要在testng.xml文件中指定這些偵聽器

請注意,@ Listeners批註將應用於您的整個套件文件,就像您在testng.xml文件中指定的同樣若是要限制其範圍(例如,僅在當前類上運行),則偵聽器中的代碼能夠首先檢查將要運行的測試方法,而後決定要作什麼。這是能夠完成的。

一、首先定義一個新的自定義註釋,可用於指定此限制:

@Retention(RetentionPolicy.RUNTIME)
@Target ({ElementType.TYPE})
public @interface DisableListener {}

二、在常規偵聽器中添加以下所示的編輯檢查:

public void beforeInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) {
  ConstructorOrMethod consOrMethod =iInvokedMethod.getTestMethod().getConstructorOrMethod();
  DisableListener disable = consOrMethod.getMethod().getDeclaringClass().getAnnotation(DisableListener.class);
  if (disable != null) {
    return;
  }
  // else resume your normal operations
}

三、註釋測試類,其中不調用偵聽器:

@DisableListener
@Listeners({ com.example.MyListener.class, com.example.MyMethodInterceptor.class })
public class MyTest {
  // ...
}

12、使用的ServiceLoader指定偵聽器

最後,JDK提供了一種很是優雅的機制,能夠經過ServiceLoader類在類路徑上指定接口的實現。
使用ServiceLoader,您要作的就是建立一個包含偵聽器和一些配置文件的jar文件,在運行TestNG時將該jar文件放在類路徑中,TestNG會自動找到它們。

這是其工做方式的具體示例。

讓咱們從建立一個監聽器開始(任何TestNG監聽器均可以工做):

package test.tmp;
 
public class TmpSuiteListener implements ISuiteListener {
  @Override
  public void onFinish(ISuite suite) {
    System.out.println("Finishing");
  }
 
  @Override
  public void onStart(ISuite suite) {
    System.out.println("Starting");
  }
}

編譯該文件,而後在META-INF / services / org.testng.ITestNGListener位置建立一個文件,該文件將命名您想要此接口的實現。
您應該以如下目錄結構結束,只有兩個文件:

$ tree
|____META-INF
| |____services
| | |____org.testng.ITestNGListener
|____test
| |____tmp
| | |____TmpSuiteListener.class
 
$ cat META-INF/services/org.testng.ITestNGListener
test.tmp.TmpSuiteListener

建立此目錄的jar:

$ jar cvf ../sl.jar .
added manifest
ignoring entry META-INF/
adding: META-INF/services/(in = 0) (out= 0)(stored 0%)
adding: META-INF/services/org.testng.ITestNGListener(in = 26) (out= 28)(deflated -7%)
adding: test/(in = 0) (out= 0)(stored 0%)
adding: test/tmp/(in = 0) (out= 0)(stored 0%)
adding: test/tmp/TmpSuiteListener.class(in = 849) (out= 470)(deflated 44%)

接下來,在調用TestNG時,將此jar文件放在類路徑中:

$ java -classpath sl.jar:testng.jar org.testng.TestNG testng-single.yaml
Starting
f2 11 2
PASSED: f2("2")
Finishing

經過此機制,您只需將jar文件添加到類路徑便可將相同的一組偵聽器應用於整個組織,而不是要求每一個開發人員都記住在其testng.xml文件中指定這些偵聽器。

十3、小結

  好了,今天關於TestNG之並行性和超時,就分享到這裏。

相關文章
相關標籤/搜索