39

I know this is bad practice, but it needs to be done, or I'll need to switch to testng. Is there a way, similar to JUnit 3's testSuite, to specify the order of the tests to be run in a class?

Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
K Poole
  • 525
  • 1
  • 5
  • 7

6 Answers6

62

I can see several reasons for doing this, especially when using JUnit to run functional tests or test persistent objects. For example, consider an object Article which is persisted to some kind of persistent storage. If I would like to test the insert, update and delete functionality on the Article object following the unit test principle "all tests should be reorderable and test only a specific part of the functionality", I would have three tests:

  • testInsertArticle()
  • testUpdateArticle()
  • testDeleteArticle()

However, to be able to test the update functionality, I would first need to insert the article. To test the delete functionality, I would also need to insert an article. So, in practice, the insert functionality is already tested both in testUpdateArticle() and testDeleteArticle(). It is then tempting to just create a test method testArticleFunctionality() which does it all, but methods like that will eventually get huge (and they won't just test part of the functionality of the Article object).

The same goes for running functional tests against for example a restful API. JUnit is great also for these cases if it wasn't for the undeterministic ordering of tests.

That said, I extended Michael D's OrderedRunner to use annotations to determine order of tests, just thought I should share. It can be extended further, for example by specifying exactly which tests each test depends on, but this is what I'm using for now.

This is how it is used. It avoids the need for naming tests like AA_testInsert(), AB_testUpdate(), AC_testDelete(), ..., ZC_testFilter(), etc.

@RunWith(OrderedRunner.class)
public class SomethingTest {
    @Test
    @Order(order=2)
    public void testUpdateArticle() {
        // test update
    }

    @Test
    @Order(order=1)
    public void testInsertArticle() {
        // test insert
    }

    @Test
    @Order(order=3)
    public void testDeleteArticle() {
        // test delete
    }
}

No matter how these tests are placed in the file, they will always be run as order=1 first, order=2 second and last order=3, no matter if you run them from inside Eclipse, using Ant, or any other way.

Implementation follows. First, the annotation Order.

@Retention(RetentionPolicy.RUNTIME)
public @interface Order {
    public int order();
}

Then, the modified OrderedRunner.

public class OrderedRunner extends BlockJUnit4ClassRunner {
    public OrderedRunner(Class<?> klass) throws InitializationError {
        super(klass);
    }

    @Override
    protected List<FrameworkMethod> computeTestMethods() {
        List<FrameworkMethod> copy = new ArrayList<>(super.computeTestMethods());
        Collections.sort(list, new Comparator<FrameworkMethod>() {
            @Override
            public int compare(FrameworkMethod f1, FrameworkMethod f2) {
                Order o1 = f1.getAnnotation(Order.class);
                Order o2 = f2.getAnnotation(Order.class);
        
                if(o1==null && o2 == null) return 0;
                if (o1 == null) return 1;
                if (o2 == null) return -1;

                return o1.order() - o2.order();
            }
        });
        return list;
    }
}
Lonzak
  • 9,334
  • 5
  • 57
  • 88
joscarsson
  • 4,789
  • 4
  • 35
  • 43
  • 4
    +1 for the annotation based ordering rather than alphabetical – Matt Sep 07 '12 at 17:39
  • 2
    Exactly what I wanted to find. Could've been better if the annotation method was `value()` and just write `@Order(1)` – lyomi Sep 26 '13 at 09:03
  • I followed your code it works great, but after importing the same code to the test cases which has 50 test case I had `initializationerror0` error. I'm using Kepler with JUnit4.10 – yashhy Dec 30 '13 at 07:26
  • Any suggestions to test this class without using mocking? – Víctor Albertos Jun 30 '16 at 10:32
  • Error in java 8 + junit 4.12 : java.util.Collections$UnmodifiableList.sort java.lang.UnsupportedOperationException. Fix `List list = new ArrayList(super.computeTestMethods());` – JRichardsz Aug 18 '20 at 20:10
62

If you're sure you really want to do this: There may be a better way, but this is all I could come up with...

JUnit4 has an annotation: @RunWith which lets you override the default Runner for your tests.

In your case you would want to create a special subclass of BlockJunit4ClassRunner, and override computeTestMethods() to return tests in the order you want them executed. For example, let's say I want to execute my tests in reverse alphabetical order:

public class OrderedRunner extends BlockJUnit4ClassRunner {

    public OrderedRunner(Class klass) throws InitializationError {
        super(klass);
    }

    @Override
    protected List computeTestMethods() {
        List list = super.computeTestMethods();
        List copy = new ArrayList(list);
        Collections.sort(copy, new Comparator() {
            public int compare(FrameworkMethod o1, FrameworkMethod o2) {
                return o2.getName().compareTo(o1.getName());
            }
        });
        return copy;
    }
}
@RunWith(OrderedRunner.class)
public class OrderOfTest {
    @Test public void testA() { System.out.println("A"); }
    @Test public void testC() { System.out.println("C"); }
    @Test public void testB() { System.out.println("B"); }
}

Running this test produces:

C
B
A

For your specific case, you would want a comparator that would sort the tests by name in the order you want them executed. (I would suggest defining the comparator using something like Google Guava's class Ordering.explicit("methodName1","methodName2").onResultOf(...); where onResultOf is provided a function that converts FrameworkMethod to its name... though obviously you are free to implement that any way you want.

Jens
  • 67,715
  • 15
  • 98
  • 113
Michael D
  • 1,932
  • 16
  • 12
26

From JUnit version 4.11 onwards, it is possible to influence the order of test execution by annotating your class with @FixMethodOrder and specifying any of the available MethodSorters. See this link for more details.

Shiva Kumar
  • 3,111
  • 1
  • 26
  • 34
4

Using junit 4.11 the new annotation @FixMethodOrder allows to set a specific order:

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
Philippe Blayo
  • 10,610
  • 14
  • 48
  • 65
  • So in my case the reason I'm looking into this --similar to the poster above and his 'article' object-- is that if the first test fails don't bother running the other ones, since they're using a feature tested by the first test. To that end what I would like is, if the first test fails, for the remaining tests to throw something like `AssumptionViolatedException` (causing the tests to be labelled as 'skipped' in my particular runner, intelliJ). I can do this with if-statements & fields, but I'm wondering if JUnit has a nicer facility built in. Any ideas? – Groostav Oct 23 '15 at 17:43
  • @Groostav There's `assumeTrue()` / `assumeThat()` etc. You can have the first test set a static boolean on the test class to `true` if it passes and then `assumeTrue(flag)` in the other tests, for example. Or you can check it once only in an `@Before` method (need another flag or a tri-state flag to skip the check the very first time before the first test has run). – Matthew Read Feb 26 '17 at 21:45
1

If you want to run junit tests in order "just as they present in your source code", and don't want to modify your tests code, see my note about this here:

How to run junit tests in order as they present in your source code

But it is really not a good idea, tests must be independent.

kornero
  • 1,109
  • 8
  • 11
1

Joscarsson and Michael D code in my github repo. I hope they don't mind. I also provide ordered version for Parameterized class. It's already to use as maven dependency

<repositories>
    <repository>
        <id>git-xxx</id>
        <url>https://github.com/crsici/OrderedRunnerJunit4.11/raw/master/</url>
    </repository>
</repositories>

<dependency>
    <groupId>com.sici.org.junit</groupId>
    <artifactId>ordered-runner</artifactId>
    <version>0.0.1-RELEASE</version>
</dependency>
Trung Pham
  • 125
  • 7