1

I am making some integration tests that load data into a remote test database before the integrations tests. However, it is a lot of data so I'd prefer to do it only once before all of my integrations tests.

I've gotten @BeforeClass/@AfterClass working with using either @RunWith(Suite.class) and JUnitCore.runClasses() to run all my test classes as a Suite. However, I am stuck on how to get Spring to Autowire resources that are needed for the setup and teardown. example:

public class AbstractTest {
    @Autowired
    private SessionFactory sf;

    @BeforeClass
    public static void setup() {
        sf.getCurrentSession().createQuery("make tables");
    }
}

But sf is always null, because @BeforeClass needs to be run from a static context. I have also tried using @ClassRule as shown here: How to share JUnit BeforeClass logic among multiple test classes but with no change;

How do I get both the @BeforeClass/@AfterClass functionality for a suite of test classes and have Autowired resources in the @BeforeClass/@AfterClass methods? Getting this to run with @Parameterized as well would be even better.

Oscar Courchaine
  • 346
  • 3
  • 14
  • Solution for null sf may be here https://stackoverflow.com/questions/2425015/how-to-access-spring-context-in-junit-tests-annotated-with-runwith-and-context – Jay Smith Jun 28 '17 at 06:02
  • 1
    If you are using `Spring Boot`, you should annotate the test class with `@RunWith(SpringRunner.class)`and `@SpringBootTest`, then Spring will deal with the dependencies for you. – Dave Pateral Jun 28 '17 at 06:25
  • Unfortunately the version of Spring that I'm locked into does not have @SpringBootTest – Oscar Courchaine Jun 28 '17 at 06:59

1 Answers1

1

SpringJUnit4ClassRunner calls BeforeClass before autowired . Context is construced ,but not injected yet , by one be injected after calling BeforeClass methods.

from RunBeforeTestClassCallbacks : 
     public void evaluate() throws Throwable {
        for (FrameworkMethod before : befores) { -- call for BeforeClass methods
            before.invokeExplosively(target);
        }
        next.evaluate(); -- call DependencyInjectionTestExecutionListener 
                          --where context is injected
}

You can try this (instead of @BeforeClass use afterPropertiesSet from InitializingBean - it's executed BeforeClass call but before all test stat be executed)

public abstract class AbstractTest implements InitializingBean{
    @Autowired
    private SessionFactory sf;

    //@BeforeClass
    @Override
    public void afterPropertiesSet() throws Exception {
        sf.getCurrentSession().createQuery("make tables");
}

or

public abstract class AbstractTest {

private static SessionFactory sf;

@Autowired
public void setSessionFactory (SessionFactory sf){
    AbstractTest.sf = sf;
    setup();
}

//@BeforeClass
public static void setup() {
    sf.getCurrentSession().createQuery("make tables");
}
}

In any case it be some work around as BeforeClass for tests classes is celled before spring context .


Variant 2 , updated

@TestExecutionListeners(listeners = {AbstractTest.class, DependencyInjectionTestExecutionListener.class})
public class AbstractTest extends AbstractTestExecutionListener {

public static SessionFactory sf;

@BeforeClass
public static void setup() {
    sf.getCurrentSession().createQuery("make tables");
}

@Override
public void beforeTestClass(TestContext testContext) throws Exception {
    sf = testContext.getApplicationContext().getBean(SessionFactory.class);
    super.beforeTestClass(testContext);
}
}
xyz
  • 5,228
  • 2
  • 26
  • 35
  • And then all of my Test classes should inherit from this abstract class? – Oscar Courchaine Jun 28 '17 at 16:14
  • yes , nut only where you want to execute query ( . you want to do something custom . spring +junit give standart way for work with beforeclass methods – xyz Jun 28 '17 at 16:17
  • Right yeah, this is only for my integration tests. Is there a similar workaround for @AfterClass or will I have to do without? – Oscar Courchaine Jun 28 '17 at 16:42
  • I added second variant .I thought about one but in our project I chose first – xyz Jun 28 '17 at 16:42
  • Is there any benefit to the second Variant? – Oscar Courchaine Jun 28 '17 at 16:43
  • for @AfterClass you shouldn't have any problem as conject is injected – xyz Jun 28 '17 at 16:43
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/147855/discussion-between-sbjavateam-and-oscar-courchaine). – xyz Jun 28 '17 at 16:43
  • second approach it's just another approach , I'm not 100% sure that transaction manager works correct . – xyz Jun 28 '17 at 16:47
  • for first variant , you base test should implement spring initialization interface and bit change junit lifecycle for beforClass , - you implement it in your way. with second approach you 100% follow junit logic but there is some "magic " that come from listener – xyz Jun 28 '17 at 16:50
  • Took me a while as I had to configure a lot behind the scenes, but I got variant 1, using InitializingBean, to work perfectly – Oscar Courchaine Jul 01 '17 at 06:01