如何根据条件禁用TestNG测试

27

目前是否有一种方法可以基于条件禁用TestNG测试

我知道您可以在TestNG中这样禁用测试:

@Test(enabled=false, group={"blah"})
public void testCurrency(){
...
}

我想根据某个条件禁用同样的测试,但不知道怎么做。类似这样:

@Test(enabled={isUk() ? false : true), group={"blah"})
public void testCurrency(){
...
}

有没有人知道这是否可行。


注释不是可执行代码,因此这种情况不太可能发生。您真正想做什么 - 在什么条件下您希望运行或不运行测试? - matt b
谢谢Matt。请查看下面Cedric的答案以获取更多细节。 - Afamee
7个回答

39

更简单的方法是在一个方法上使用@BeforeMethod注释,该方法检查您的条件。如果您想跳过测试,只需抛出SkipException即可。像这样:

@BeforeMethod
protected void checkEnvironment() {
  if (!resourceAvailable) {
    throw new SkipException("Skipping tests because resource was not available.");
  }
}

1
嗨,从6.13版本开始,抛出SkipException会将状态设置为失败,而不是跳过。请参见https://dev59.com/SHnZa4cB1Zd3GeqPnTht#47571922。 - Mike G.
SkipException 的文档提到了已弃用的 '@Configuration' 注释。这可能解释了 '@BeforeMethod' 等的行为描述。 - dpr
“enabled=false” 的检查难道不是在 BeforeMethod 测试的更早阶段发生吗?在某些情况下,使用 “enabled=false” 比使用 Before-hook 更好,我个人认为。 - djangofan

16
您有两个选择:

您的注释转换器将测试条件,然后在不满足条件时覆盖@Test注释以添加属性“enabled=false”。


谢谢 Cedric。我想我会喜欢探索“注释转换器”选项。那听起来更像是我要找的东西。 - Afamee
再次感谢。我很快就得到了这个转换器的一个工作示例。但有一件事情并不像我预期的那样。我想通过调用annot.setTestName(concatString)来动态地转换我运行的测试的名称(至少在测试结果中显示的方式),其中annot表示方法注释,但结果返回的原始名称没有改变。还有其他的方法吗?希望没有让您困惑。 - Afamee
你无法在运行时覆盖类的行为,这就是 Java 的设计方式。相反,我建议您将决定那个名称的逻辑直接放入测试中,以便可以在 getTestName() 中返回它。 - Cedric Beust
这正是我所做的。我认为 setTestName("newName") 应该是用来更改测试名称的。我知道调用 getTestName() 是基于测试代码逻辑获取名称,但然后我想通过说 annot.setTestName(newName) 或 annot.setTestName(getTestName()+"_Modified") 来设置这个新检索到的测试名称。当测试完成时,它仍然具有原始名称而不是修改后的名称。 - Afamee
这可以实现吗?如果TestA失败了,我想禁用TestB。 - Rameshwar

10

我知道两种方法可以让你控制在TestNG中“禁用”测试。

需要注意的区别是:SkipException会退出所有后续测试,而实现IAnnotationTransformer使用反射来禁用个别测试,根据您指定的条件。我将解释SkipException和IAnnotationTransfomer的用法。

SKIP Exception示例

import org.testng.*;
import org.testng.annotations.*;

public class TestSuite
{
    // You set this however you like.
    boolean myCondition;
    
    // Execute before each test is run
    @BeforeMethod
    public void before(Method methodName){
        // check condition, note once you condition is met the rest of the tests will be skipped as well
        if(myCondition)
            throw new SkipException();
    }
    
    @Test(priority = 1)
    public void test1(){}
    
    @Test(priority = 2)
    public void test2(){}
    
    @Test(priority = 3)
    public void test3(){}
}

IAnnotationTransformer 示例

这个示例有点复杂,但其背后的思想是一个被称为反射的概念。

维基 - http://en.wikipedia.org/wiki/Reflection_(computer_programming)

首先实现IAnnotation接口,将其保存在*.java文件中。

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import org.testng.IAnnotationTransformer;
import org.testng.annotations.ITestAnnotation;

public class Transformer implements IAnnotationTransformer {

    // Do not worry about calling this method as testNG calls it behind the scenes before EVERY method (or test).
    // It will disable single tests, not the entire suite like SkipException
    public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod){

        // If we have chose not to run this test then disable it.
        if (disableMe()){
            annotation.setEnabled(false);
        }
    }

    // logic YOU control
    private boolean disableMe() {
    }
}

然后在你的测试套件 Java 文件中,在 @BeforeClass 函数中执行以下操作:

import org.testng.*;
import org.testng.annotations.*;

/* Execute before the tests run. */    
@BeforeClass
public void before(){

    TestNG testNG = new TestNG();
    testNG.setAnnotationTransformer(new Transformer());
}

@Test(priority = 1)
public void test1(){}

@Test(priority = 2)
public void test2(){}

@Test(priority = 3)
public void test3(){}

最后一步是确保您在build.xml文件中添加一个监听器。 我的最终结果看起来像这样,这只是build.xml中的一行:


One last step is to ensure that you add a listener in your build.xml file. Mine ended up looking like this, this is just a single line from the build.xml:
<testng classpath="${test.classpath}:${build.dir}" outputdir="${report.dir}" 
    haltonfailure="false" useDefaultListeners="true"
    listeners="org.uncommons.reportng.HTMLReporter,org.uncommons.reportng.JUnitXMLReporter,Transformer" 
    classpathref="reportnglibs"></testng>

3

我更喜欢使用基于注解的方式,根据环境设置来禁用/跳过一些测试。易于维护,不需要任何特殊的编码技巧。

  • 使用 IInvokedMethodListener 接口
  • 创建自定义注解,例如:@SkipInHeadlessMode
  • 抛出 SkipException
public class ConditionalSkipTestAnalyzer implements IInvokedMethodListener {
    protected static PropertiesHandler properties = new PropertiesHandler();

    @Override
    public void beforeInvocation(IInvokedMethod invokedMethod, ITestResult result) {
        Method method = result.getMethod().getConstructorOrMethod().getMethod();
        if (method == null) {
            return;
        }
        if (method.isAnnotationPresent(SkipInHeadlessMode.class)
                && properties.isHeadlessMode()) {
            throw new SkipException("These Tests shouldn't be run in HEADLESS mode!");
        }
    }

    @Override
    public void afterInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) {
        //Auto generated
    }
}

查看详细信息:https://www.lenar.io/skip-testng-tests-based-condition-using-iinvokedmethodlistener/


3

第三种选择也可以是假设TestNG中的假设 - 当一个假设失败时,TestNG将被指示忽略测试用例,因此不会执行它。

  • 使用@Assumption注释
  • 使用AssumptionListener使用Assumes.assumeThat(...)方法

您可以使用此示例:在 TestNG 中有条件地运行测试


0
SkipException:在类中只有一个@Test方法的情况下非常有用。例如,对于数据驱动框架,我只有一个测试方法需要根据某些条件执行或跳过。因此,我将检查条件的逻辑放在@Test方法内部,并获得所需的结果。这帮助我获得了Extent报告,其中包括测试用例的通过/失败以及特定的跳过情况。

0
在使用带有 @BeforeMethod 注释的方法中抛出 SkipException 并不能起到效果,因为它会跳过测试套件中剩余所有测试而不考虑是否对这些测试抛出了 SkipException
我没有彻底调查它,但我找到了另一种方法:在 @Test 注释上使用 dependsOnMethods 属性。
import org.testng.SkipException;
import org.testng.annotations.Test;

public class MyTest {

  private boolean conditionX = true;
  private boolean conditionY = false;

  @Test
  public void isConditionX(){
    if(!conditionX){
      throw new SkipException("skipped because of X is false");
    }
  }

  @Test
  public void isConditionY(){
    if(!conditionY){
      throw new SkipException("skipped because of Y is false");
    }
  }

  @Test(dependsOnMethods="isConditionX")
  public void test1(){

  }

  @Test(dependsOnMethods="isConditionY")
  public void test2(){

  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接