Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Specifying an order to junit 4 tests at the Method level (not class level)

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?

like image 343
K Poole Avatar asked Jun 21 '10 23:06

K Poole


People also ask

Which annotation in JUnit 4 is used for changing the execution order of the test scenarios?

JUnit 4: @FixedMethodOrder, Class MethodSorters. Starting JUnit 4.11, we have the annotation @FixMethodOrder and MethodSorters. class supporting the facility of setting an order for a test's execution.

Which JUnit annotation allows you to define order of execution for the test methods?

Annotation Type FixMethodOrder. This class allows the user to choose the order of execution of the methods within a test class.

Can we prioritize tests in JUnit?

In general, you can't specify the order that separate unit tests run in (though you could specify priorities in TestNG and have a different priority for each test). However, unit tests should be able to be run in isolation, so the order of the tests should not matter.


2 Answers

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.

like image 135
Michael D Avatar answered Oct 11 '22 08:10

Michael D


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;     } } 
like image 26
joscarsson Avatar answered Oct 11 '22 07:10

joscarsson