有条件地忽略 JUnit 4 中的测试

Posted

技术标签:

【中文标题】有条件地忽略 JUnit 4 中的测试【英文标题】:Conditionally ignoring tests in JUnit 4 【发布时间】:2010-12-13 22:32:22 【问题描述】:

好的,所以@Ignore 注释非常适合标记不应运行的测试用例。

但是,有时我想忽略基于运行时信息的测试。例如,如果我有一个并发测试需要在具有一定数量内核的机器上运行。如果这个测试是在单处理器机器上运行的,我认为只通过测试是不正确的(因为它还没有运行),而且测试失败并破坏构建肯定是不对的.

所以我希望能够在运行时忽略测试,因为这似乎是正确的结果(因为测试框架将允许构建通过但记录测试未运行)。我相当确定注释不会给我这种灵活性,并且怀疑我需要为相关类手动创建测试套件。但是,文档没有提及任何关于此的内容,并且通过 API 查看它也不清楚如何以编程方式完成(即我如何以编程方式创建 Test 的实例或类似的实例,该实例相当于由@Ignore 注释?)。

如果有人过去做过类似的事情,或者对我还能如何解决这个问题有一个好主意,我很乐意听到。

【问题讨论】:

【参考方案1】:

除了@tkruse 和@Yishai 的回答: 我这样做是为了有条件地跳过测试方法,特别是对于 Parameterized 测试,如果测试方法应该只针对某些测试数据记录运行。

public class MyTest 
    // get current test method
    @Rule public TestName testName = new TestName();
    
    @Before
    public void setUp() 
        org.junit.Assume.assumeTrue(new Function<String, Boolean>() 
          @Override
          public Boolean apply(String testMethod) 
            if (testMethod.startsWith("testMyMethod")) 
              return <some condition>;
            
            return true;
          
        .apply(testName.getMethodName()));
        
        ... continue setup ...
    

【讨论】:

【参考方案2】:

JUnit 在运行时执行此操作的方法是 org.junit.Assume

 @Before
 public void beforeMethod() 
     org.junit.Assume.assumeTrue(someCondition());
     // rest of setup.
 

您可以在@Before 方法或测试本身中执行此操作,但不能在@After 方法中执行。如果您在测试本身中执行此操作,您的 @Before 方法将运行。您也可以在@BeforeClass 内执行此操作,以防止类初始化。

假设失败导致测试被忽略。

编辑:为了与 junit-ext 中的 @RunIf 注释进行比较,他们的示例代码如下所示:

@Test
public void calculateTotalSalary() 
    assumeThat(Database.connect(), is(notNull()));
    //test code below.

更不用说通过这种方式从Database.connect() 方法捕获和使用连接要容易得多。

【讨论】:

@notnoop,这根本不是我的观察。他们被忽略了。 IDEA 测试运行程序以这种方式报告它们,查看 JUnit 源代码显示它报告测试为忽略。 引用:“将来,这可能会改变,失败的假设可能会导致测试被忽略。”它实际上改变了,我相信从 4.5 开始。当前的 javadoc 说:“默认的 JUnit 运行器将具有失败假设的测试视为被忽略。自定义运行器的行为可能不同。” github.com/KentBeck/junit/blob/… Eclipse 3.6 和 Junit 4.8.1 报告错误假设作为通过测试。与 ant 1.8.1 相同。 Eclipse 将失败的假设报告为通过是一个错误:bugs.eclipse.org/bugs/show_bug.cgi?id=359944 @JeffStorey,那么您正在寻找一些东西。一个是@BeforeClass 注释,在那里你可以让你的假设失败,这将跳过整个课程。另一个是@ClassRule(用于细粒度控制,但在整个班级,一次)。【参考方案3】:

在 JUnit 4 中,您的另一个选择可能是创建一个注释以表示测试需要满足您的自定义标准,然后使用您自己的和使用反射扩展默认运行程序,根据自定义标准做出决定。它可能看起来像这样:

public class CustomRunner extends BlockJUnit4ClassRunner 
    public CTRunner(Class<?> klass) throws initializationError 
        super(klass);
    

    @Override
    protected boolean isIgnored(FrameworkMethod child) 
        if(shouldIgnore()) 
            return true;
        
        return super.isIgnored(child);
    

    private boolean shouldIgnore(class) 
        /* some custom criteria */
    

【讨论】:

虽然这看起来不错而且很干净,但它不适用于 JUnit4 的当前版本,因为 BlockJUnit4ClassRunner 不再提供 isIgnored 方法。【参考方案4】:

快速说明:Assume.assumeTrue(condition) 忽略其余步骤但通过了测试。 要使测试失败,请在条件语句中使用org.junit.Assert.fail()。与Assume.assumeTrue() 一样工作,但未通过测试。

【讨论】:

如上述答案中所述,失败的假设不会导致测试通过,它会返回单独的状态。一些运行程序可能会错误地报告这个好像它是通过,但这是测试运行程序中的一个弱点/错误(并且默认的 JUnit 运行程序将测试显示为忽略)。至于你的最后一句话,考试不及格并不是我想要做的。 哦,好的。在我的情况下,测试通过了失败的假设,但我希望它们被报告为失败(我正在检查来自 Test Watcher 的异常)。强迫失败对我有帮助。【参考方案5】:

您应该签出Junit-ext 项目。他们有执行条件测试的RunIf 注释,例如:

@Test
@RunIf(DatabaseIsConnected.class)
public void calculateTotalSalary() 
    //your code there


class DatabaseIsConnected implements Checker 
   public boolean satisify() 
        return Database.connect() != null;
   

[取自他们教程的代码示例]

【讨论】:

感谢您的回答 - 一种有趣的功能替代语法,但我将直接使用 Assume,以免引入另一个依赖项。 我个人更喜欢这个解决方案。如果您有许多测试应该基于相同的条件运行,这将比在每个测试中都使用 Assume 更理想。另外,如果这可以用在类级别而不是方法级别,那就更理想了。 我更喜欢它,因为这有助于在运行时有条件地运行测试。它适合要运行多个单元测试的地方,并且要求是在特定检查器上运行单元测试。我真的很惊讶看到 junit-ext 在 maven 存储库中不可用。我们如何在 maven 项目中利用这一点。 @RunIf 这样的注释将测试应该运行的条件与实际测试代码分开,我认为这很好。我不喜欢的是它需要一个特定的测试运行器。因此我写了一个JUnit rule 来条件性地忽略测试。 在我们的本地存储库中安装 junit-ext jar(在此处找到 code.google.com/p/junit-ext/downloads/… )并实现此 @RunIf 注释后......什么都没有!它完全被忽略了,我认为原因可能是 junit-ext 似乎依赖于 junit 4.5。由于弹簧测试,我们需要 4.9+。所以……没关系。

以上是关于有条件地忽略 JUnit 4 中的测试的主要内容,如果未能解决你的问题,请参考以下文章

JUnit 4中的人类可读测试名称

JUnitJUnit 假设测试

用于忽略 JUnit 测试的 FindBugs 过滤器文件

junit单元测试注意的问题

IntelliJ 忽略 JUnit 5

JUnit4