Skip to content

Instantly share code, notes, and snippets.

@rherrmann
Last active August 15, 2024 15:22
Show Gist options
  • Save rherrmann/7447571 to your computer and use it in GitHub Desktop.
Save rherrmann/7447571 to your computer and use it in GitHub Desktop.
JUnit rule to conditionally ignore test cases (see also http://www.codeaffine.com/2013/11/18/a-junit-rule-to-conditionally-ignore-tests/)
/*******************************************************************************
* Copyright (c) 2013,2014 Rüdiger Herrmann
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Rüdiger Herrmann - initial API and implementation
* Matt Morrissette - allow to use non-static inner IgnoreConditions
******************************************************************************/
package com.codeaffine.test;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Modifier;
import org.junit.Assume;
import org.junit.rules.MethodRule;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;
public class ConditionalIgnoreRule implements MethodRule {
public interface IgnoreCondition {
boolean isSatisfied();
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface ConditionalIgnore {
Class<? extends IgnoreCondition> condition();
}
@Override
public Statement apply( Statement base, FrameworkMethod method, Object target ) {
Statement result = base;
if( hasConditionalIgnoreAnnotation( method ) ) {
IgnoreCondition condition = getIgnoreContition( target, method );
if( condition.isSatisfied() ) {
result = new IgnoreStatement( condition );
}
}
return result;
}
private static boolean hasConditionalIgnoreAnnotation( FrameworkMethod method ) {
return method.getAnnotation( ConditionalIgnore.class ) != null;
}
private static IgnoreCondition getIgnoreContition( Object target, FrameworkMethod method ) {
ConditionalIgnore annotation = method.getAnnotation( ConditionalIgnore.class );
return new IgnoreConditionCreator( target, annotation ).create();
}
private static class IgnoreConditionCreator {
private final Object target;
private final Class<? extends IgnoreCondition> conditionType;
IgnoreConditionCreator( Object target, ConditionalIgnore annotation ) {
this.target = target;
this.conditionType = annotation.condition();
}
IgnoreCondition create() {
checkConditionType();
try {
return createCondition();
} catch( RuntimeException re ) {
throw re;
} catch( Exception e ) {
throw new RuntimeException( e );
}
}
private IgnoreCondition createCondition() throws Exception {
IgnoreCondition result;
if( isConditionTypeStandalone() ) {
result = conditionType.newInstance();
} else {
result = conditionType.getDeclaredConstructor( target.getClass() ).newInstance( target );
}
return result;
}
private void checkConditionType() {
if( !isConditionTypeStandalone() && !isConditionTypeDeclaredInTarget() ) {
String msg
= "Conditional class '%s' is a member class "
+ "but was not declared inside the test case using it.\n"
+ "Either make this class a static class, "
+ "standalone class (by declaring it in it's own file) "
+ "or move it inside the test case using it";
throw new IllegalArgumentException( String.format ( msg, conditionType.getName() ) );
}
}
private boolean isConditionTypeStandalone() {
return !conditionType.isMemberClass() || Modifier.isStatic( conditionType.getModifiers() );
}
private boolean isConditionTypeDeclaredInTarget() {
return target.getClass().isAssignableFrom( conditionType.getDeclaringClass() );
}
}
private static class IgnoreStatement extends Statement {
private final IgnoreCondition condition;
IgnoreStatement( IgnoreCondition condition ) {
this.condition = condition;
}
@Override
public void evaluate() {
Assume.assumeTrue( "Ignored by " + condition.getClass().getSimpleName(), false );
}
}
}
@SiKing
Copy link

SiKing commented Apr 2, 2014

Does this work with any JUnit4? I am trying this with JUnit4.11, and the exact condition you described at http://www.codeaffine.com/2013/11/18/a-junit-rule-to-conditionally-ignore-tests/ All I get is: java.lang.RuntimeException: java.lang.InstantiationException: xyzTest$NotRunningOnWindows
at com.codeaffine.junit.ignore.ConditionalIgnoreRule.newCondition(ConditionalIgnoreRule.java:61)
at com.codeaffine.junit.ignore.ConditionalIgnoreRule.getIgnoreContition(ConditionalIgnoreRule.java:52)
at com.codeaffine.junit.ignore.ConditionalIgnoreRule.apply(ConditionalIgnoreRule.java:38)
at org.junit.runners.BlockJUnit4ClassRunner.withMethodRules(BlockJUnit4ClassRunner.java:349)
at org.junit.runners.BlockJUnit4ClassRunner.withRules(BlockJUnit4ClassRunner.java:339)
at org.junit.runners.BlockJUnit4ClassRunner.methodBlock(BlockJUnit4ClassRunner.java:256)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.lang.InstantiationException: net.sourceforge.genieos.demos.WindowsLaunchTest$NotRunningOnWindows
at java.lang.Class.newInstance0(Class.java:359)
at java.lang.Class.newInstance(Class.java:327)
at com.codeaffine.junit.ignore.ConditionalIgnoreRule.newCondition(ConditionalIgnoreRule.java:57)
... 21 more

@SiKing
Copy link

SiKing commented Apr 2, 2014

Upon further investigation I dicovered that having a @before setUp() causes this failure. If I remove the @ConditionalIgnore from my test, it passes just fine. The setUp() - with the @ConditionalIgnore - always fails.
My source is here: https://sourceforge.net/p/genieos/code-0/HEAD/tree/trunk/demos/src/test/java/net/sourceforge/genieos/demos/WindowsLaunchTest.java
Any idea?
TIA.

@SiKing
Copy link

SiKing commented Apr 3, 2014

Just tried to run this on my Linux machine: the @before setUp() is still run.
Am I using this wrong?

@SiKing
Copy link

SiKing commented Apr 4, 2014

Found my issue: I declared the NotRunningOnWindows class inside of my SomeTest class, and so it has to be explicitly static.

@yinzara
Copy link

yinzara commented Apr 4, 2014

I have fixed the above class to work with classes declared inside your test case (i.e. anonymous inner member classes). The fix is in my fork of this Gist available at: https://gist.github.com/yinzara/9980184

@smesh
Copy link

smesh commented Dec 24, 2015

https://issues.apache.org/jira/browse/GEODE-167 - ConditionalIgnore from Apache.

@navnathkarche
Copy link

I tried this solution with Espresso but doesn't work.

Following is my observation:
If I have four test cases: A(), B(), C(), D() and I applied @ConditionalIgnore on B() method, then C() and D() also ignored.

Can you please help me on this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment