Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use different Junit TestRunner in Eclipse and Ant?

I have a couple of JBehave tests that I want to run from Eclipse and Ant. In Eclipse I want to see a tree of all the different stories, scenarios and steps that are performed in the graphical output, so I added a custom runner to the tests that does this:

@RunWith(de.codecentric.jbehave.junit.monitoring.JUnitReportingRunner.class)
public class MyStoryTest extends org.jbehave.core.junit.JUnitStories
{
    // ...
}

But on contrary when running the tests with Ant and in the Continuous Integration server I want to see only every whole story as a single item in the output. This is usually achieved without any annotation:

public class MyStoryTest extends JUnitStories
{
    // ...
}

So how can I tell Ant (junit Ant task) to use a different runner than Eclipse? To make things more complicated: At the moment I use a test suite in Eclipse (not in Ant) to run the tests:

@RunWith(org.junit.extensions.cpsuite.ClasspathSuite.class)
@org.junit.extensions.cpsuite.ClassnameFilters("foo.mypackage.tests.*")
public class MyStoriesTestSuite
{
    // Nothing more to say ;)
}

Any Ideas?

Cheers, Tilmann

like image 868
Gandalf Avatar asked Nov 09 '12 13:11

Gandalf


People also ask

What is TestRunner in JUnit?

A command line based tool to run tests. java junit.textui.TestRunner [-wait] TestCaseClass. TestRunner expects the name of a TestCase class as argument. If this class defines a static suite method it will be invoked and the returned test is run. Otherwise all the methods starting with "test" having no arguments are run ...

What is the use of @RunWith in JUnit?

RunWith' imports @RunWith annotation from the Junit class. @RunWith annotation tells JUnit that tests should run using Cucumber class present in 'Cucumber. api. junit' package.


1 Answers

I did some hack a few weeks ago, that can fit your needs. I realized, that the java command, which is executed by Eclipse in case of a unit test run contains always a package in its name. So if this gives back true, probably you are running your test under Eclipse:

System.getProperty( "sun.java.command" ).contains( "org.eclipse.jdt" )

I know, its not 100 percent solution, but usually works, and its better than nothing.

I created and tested a Runner+Annotation pair for you:

Annotation:

package org.junit.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.junit.runner.Runner;
import org.junit.runners.JUnit4;

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target( ElementType.TYPE )
public @interface RunWithInEnvironment {
    Class<? extends Runner> eclipse();
    Class<? extends Runner> defaultRunner() default JUnit4.class;
}

By default it uses JUnit4 as defaultrunner, which is really the default for JUnit4.

The Runner, which uses the information of the annotation:

package org.junit.runners;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

import org.junit.annotation.RunWithInEnvironment;
import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runner.notification.RunNotifier;

public class EnvironmentDependentRunner extends Runner {
    protected Class<?> testClass;
    protected Runner delegate;

    public EnvironmentDependentRunner(Class<?> testClass) {
        super();
        this.testClass = testClass;
        RunWithInEnvironment annotation = findAnnotationInClassHierarchy(testClass);
        assertNotNull( EnvironmentDependentRunner.class.getSimpleName() + " can be used only with test classes, that are annotated with " + RunWithInEnvironment.class.getSimpleName() + " annotation somewhere in their class hierarchy!", annotation );
        Class<? extends Runner> delegateClass = null;
        if ( System.getProperty( "sun.java.command" ).contains( "org.eclipse.jdt" ) && annotation.eclipse() != null ) {
            delegateClass = annotation.eclipse();
        }
        else {
            delegateClass = annotation.defaultRunner();
        }
        try {
            Constructor<? extends Runner> constructor = delegateClass.getConstructor( Class.class );
            delegate = constructor.newInstance(testClass);
        } catch (NoSuchMethodException e) {
            fail( delegateClass.getName() + " must contain a public constructor with a " + Class.class.getName() + " argument.");
        } catch (SecurityException e) {
            throw new RuntimeException("SecurityException during instantiation of " + delegateClass.getName() );
        } catch (InstantiationException e) {
            throw new RuntimeException("Error while creating " + delegateClass.getName() );
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Error while creating " + delegateClass.getName() );
        } catch (IllegalArgumentException e) {
            throw new RuntimeException("Error while creating " + delegateClass.getName() );
        } catch (InvocationTargetException e) {
            throw new RuntimeException("Error while creating " + delegateClass.getName() );
        }
    }

    private RunWithInEnvironment findAnnotationInClassHierarchy(Class<?> testClass) {
        RunWithInEnvironment annotation = testClass.getAnnotation(RunWithInEnvironment.class);
        if (annotation != null) {
            return annotation;
        }

        Class<?> superClass = testClass.getSuperclass();
        if (superClass != null) {
            return findAnnotationInClassHierarchy(superClass);
        }

        return null;
    }

    @Override
    public Description getDescription() {
        return delegate.getDescription();
    }

    @Override
    public void run(RunNotifier arg0) {
        delegate.run(arg0);
    }
}

And an usage example:

@RunWithInEnvironment( eclipse=JUnit4.class, defaultRunner=Parameterized.class)
@RunWith( EnvironmentDependentRunner.class)
public class FooTest {
...
}

So this test will run with JUnit4 runner in Eclipse, with Parameterized outside Eclipse.

like image 200
Gábor Lipták Avatar answered Sep 30 '22 13:09

Gábor Lipták