9

What is the difference between using JUnit @BeforeClass and the Spring @TestExecutionListener beforeTestClass(TestContext testContext) "hook"? If there is a difference, which one to use under which circumstances?

Maven Dependencies:
spring-core:3.0.6.RELEASE
spring-context:3.0.6.RELEASE
spring-test:3.0.6.RELEASE
spring-data-commons-core:1.2.0.M1
spring-data-mongodb:1.0.0.M4
mongo-java-driver:2.7.3
junit:4.9
cglib:2.2

Using JUnit @BeforeClass annotation:

import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.Assert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;

@ContextConfiguration(locations = { "classpath:test-config.xml" })
public class TestNothing extends AbstractJUnit4SpringContextTests {

    @Autowired
    PersonRepository repo;

    @BeforeClass
    public static void runBefore() {
        System.out.println("@BeforeClass: set up.");
    }

    @Test
    public void testInit() {
        Assert.assertTrue(repo.findAll().size() == 0 );
    }
}

=> @BeforeClass: set up.
=> Process finished with exit code 0

Using the Spring hook:

(1) Override beforeTestClass(TextContext testContext):

import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.AbstractTestExecutionListener;

public class BeforeClassHook extends AbstractTestExecutionListener {

    public BeforeClassHook() { }

    @Override
    public void beforeTestClass(TestContext testContext) {
        System.out.println("BeforeClassHook.beforeTestClass(): set up.");
    }
}

(2) Use @TestExecutionListeners annotation:

import org.springframework.test.context.TestExecutionListeners;  
// other imports are the same    

@ContextConfiguration(locations = { "classpath:test-config.xml" })
@TestExecutionListeners(BeforeClassHook.class)
public class TestNothing extends AbstractJUnit4SpringContextTests {

    @Autowired
    PersonRepository repo;

    @Test
    public void testInit() {
        Assert.assertTrue(repo.findAll().size() == 0 );
    }
}

=> BeforeClassHook.beforeTestClass(): set up.
=> Process finished with exit code 0
rallison
  • 199
  • 2
  • 3
  • 9

2 Answers2

21

TestExecutionListeners are a way to externalize reusable code that instruments your tests.

As such, if you implement a TestExecutionListener you can reuse it across test class hierarchies and potentially across projects, depending on your needs.

On the flip side, a @BeforeClass method can naturally only be used within a single test class hierarchy.

Note, however, that JUnit also supports Rules: if you implement org.junit.rules.TestRule you can declare it as a @ClassRule to achieve the same thing... with the added benefit that a JUnit Rule can be reused just like a Spring TestExecutionListener.

So it really depends on your use case. If you only need to use the "before class" functionality in a single test class or a single test class hierarchy, then you'd be better off going the simple route of just implementing a @BeforeClass method. However, if you foresee that you will need the "before class" functionality in different test class hierarchies or across projects, you should consider implementing a custom TestExecutionListener or JUnit Rule.

The benefit of a Spring TestExecutionListener over a JUnit Rule is that a TestExecutionListener has access to the TestContext and therefore access to the Spring ApplicationContext which a JUnit Rule would not have access to. Furthermore, a TestExecutionListener can be automatically discovered and ordered.

Related Resources:

Regards,

Sam (author of the Spring TestContext Framework)

Sam Brannen
  • 29,611
  • 5
  • 104
  • 136
  • 1
    Is there any way to set the order of the listeners? (E.g. I want to run some code before the loading of the spring context happens) – Wim Deblauwe Jun 11 '14 at 13:14
  • Yes, you can control the order of the listeners when you declare `@TestExecutionListeners` on your test class. See slides 27-33 in my [Spring 3.1 and MVC Testing Support](http://www.slideshare.net/sbrannen/spring-31-and-mvc-testing-support) presentation, and consult the Javadoc for [@TestExecutionListeners](http://docs.spring.io/spring/docs/4.0.5.RELEASE/javadoc-api/org/springframework/test/context/TestExecutionListeners.html). – Sam Brannen Jun 11 '14 at 15:09
  • Just read up on both references, but I don't see anything related to the order. Are they supposed to run in the order you declare them on the annotation? – Wim Deblauwe Jun 12 '14 at 07:03
  • Yes, the `TestContextManager` maintains them as `List testExecutionListeners`, which is an ordered list that is constructed based on the order in which the listeners are declared via `@TestExecutionListeners`. – Sam Brannen Jun 12 '14 at 11:10
  • And what if you use inheritance of listeners? – Wim Deblauwe Jun 12 '14 at 11:21
  • Please consult the Javadoc for [@TestExecutionListeners#inheritListeners](http://docs.spring.io/spring/docs/4.0.5.RELEASE/javadoc-api/org/springframework/test/context/TestExecutionListeners.html#inheritListeners--). – Sam Brannen Jun 13 '14 at 14:18
2

The first solution with @BeforeClass doesn't have application context loaded. I did exteneded AbstractJUnit4SpringContextTests and defined @ContextConfiguration. I think listner is the only way to get context loaded before @beforeclass method. Or even better extending SpringJUnit4ClassRunner class as mentioned here

nir
  • 3,743
  • 4
  • 39
  • 63