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之并行性和超时,就分享到这里。

相关文章
相关标签/搜索