Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to defer calling @PostConstruct until jUnit has setup test context

I have a static Spring 3.2.4 bean with a protected @PostConstruct method that loads data from a DB when initializing.

When creating a jUnit test, in my test methods, I would like to setup the data in the DB to appropriately test the bean. However, given that the bean is instantiated prior to my test methods, I don't know how to request Spring to defer instantiation of the bean until the method is complete.

Given that the @PostConstruct method is protected, I cannot call it directly to re-initialize the bean, unless I use reflection.

Is there another way to do this, or is reflection the only way? Does Spring have any Util classes to make it easier or do I have to use standard java reflection?

like image 456
Eric B. Avatar asked Nov 01 '22 12:11

Eric B.


1 Answers

You can always start the context programmatically for such use case. Be aware that you're in charge of the lifecycle of the context in this case. The following pseudo-code illustrates this:

@Test
public void yourTest() {
    // setup your database

    ConfigurableApplicationContext context =
        new ClassPathXmlApplicationContext("/org/foo/your-context.xml");
    // Or new AnnotationConfigApplicationContext(YourConfig.class)
    try {
        YourBean bean = context.getBean("beanId");
        // Assertions
    } finally {
        context.close();
    }
}

You probably need Spring to initialize your database. You could for instance use the regular Spring test context support to initialize only the beans you require for the database setup and start another context programmatically to assert your service. If that context needs some services that were used for the database initialization, you can start a child context instead, something like

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration // for instance FooTest-context.xml
public class FooTest {

    @Autowired
    private ApplicationContext mainContext;

    @Test
    public void yourTest() {
        // setup your database

        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext();
        context.setParent(mainContext);
        context.setConfigLocation("/org/foo/your-context.xml");
        context.refresh();
        try {
            YourBean bean = context.getBean("beanId");
            // Assertions
        } finally {
            context.close();
        }
    }
}

If that's becoming a recurrent use case you can create a template method that start the container and invoke a callback interface. That way you can share the context lifecycle management at a central place.

like image 129
Stephane Nicoll Avatar answered Nov 09 '22 08:11

Stephane Nicoll