How to Skip @Postconstruct When Unit Testing

How to skip @PostConstruct when unit testing

The method SalesDataAggregate is running on startup because of the @PostConstruct annotation. If you want to keep it from running during tests you can create the class containing the post construct in your test folder and add the @primary annotation so it takes precedence over the class in your main project.

@Primary
public class ClassContainingPostConstruct{

}

How to disable @PostConstruct in Spring during Test

Since you are not testing FileListService but a depending class, you can mock it for tests. Make a mock version in a separate test package which is scanned only by test context. Mark it with @Primary annotation so it takes precedence over production version.

How to automatically disable a Spring bean when running a unit test?

A prettier way to handle this would be

@Service
public class BackgroundTaskService {

@PostConstruct
@Profile("!test")
public void startTask() {
// ...
}
}

or even

@PostConstruct
public void startTask() {
if(env.acceptsProfiles("!test")) { // env is @Autowired Environment
// start task
}
}

Only if the test profile is not active, the @PostConstruct method is run. In a Spring environment you want to use the tools Spring gives you, so use a profile instead of some custom indicator.

Testing spring bean with post construct

If you want to write a unit test of A, then don't use Spring. Instead, instantiate A yourself and pass a stub/mock of B (either by using constructor injection or ReflectionTestUtils to set the private field).

For example:

@Service
public class A {

private final B b;

@Autowired
public A(B b) {
this.b = b;
}

@PostConstruct
public void setup() {
b.call(param);
}
}

-

public class Test {

@Test
public void test() throws Exception {
B b = mock(b);
A a = new A(b);
// write some tests for A
}

}

If you have to use Spring, because you want to write an integration test, use a different application context, where you replace B with a stub/mock.

For example, assuming B is instantiated in a Production class like this:

@Configuration
public class Production {

@Bean
public B b() {
return new B();
}

}

Write another @Configuration class for your tests:

@Configuration
public class Tests {

@Bean
public B b() {
// using Mockito is just an example
B b = Mockito.mock(B.class);
Mockito.when(b).thenReturn("smth");
return b;
}

}

Reference it in your test with the @SpringApplicationConfiguration annotation:

@SpringApplicationConfiguration(classes = { Application.class, Tests.class })

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

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.

How to exclude a Spring Boot Component from running in JUnit unit tests?

The @MockBean annotation replaces a bean with another bean implemented by a Mockito mock. You're not going to stub any methods on the mock. You only care that the bean is replaced with a bean that does nothing. Inside your test class:

@SpringBootTest
class MyTest {

@MockBean
private DatabaseInitialization databaseInitialization;


Related Topics



Leave a reply



Submit