Make #SpringBootTest use a new application on every test - spring

Is there any way to make Spring boot use a completely fresh ApplicationContext on every single #Test method execution and discard the previous application context ?
Anyway to change the default behavior of reusing ApplicationContext ?

You can annotate a test method with #DirtiesContext to indicate the ApplicationContext after running this test method is dirty such that when it executes the next test method , it will completely refresh the ApplicationContext :
#SpringBootTest
public class FooTest {
#Test
#DirtiesContext
public void test1() {
}
#Test
#DirtiesContext
public void test2() {
}
}

Related

How to avoid a second Instantiation of a spring bean in child test context

I created an Embedded Sftp server bean for my integration tests, i hooked the startup and the shutdown of the server respectively with the afterPropertiesSet and destroy life cycles
public class EmbeddedSftpServer implements InitializingBean, DisposableBean {
//other class content
#Override
public void afterPropertiesSet() throws Exception {
//Code for starting server here
}
#Override
public void destroy() throws Exception {
//Code for stopping server here
}
}
here my config class
#TestConfiguration
public class SftpTestConfig {
#Bean
public EmbeddedSftpServer embeddedSftpServer() {
return new EmbeddedSftpServer();
}
//Other bean definitions
}
Now when i inject the bean in my test classes like the following :
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = SftpTestConfig .class)
class ExampleOneIT {
#Autowired
private EmbeddedSftpServer embeddedSftpServer;
}
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = SftpTestConfig .class)
class ExampleTwoIT {
#Autowired
private EmbeddedSftpServer embeddedSftpServer;
}
#SpringBatchTest
#ContextConfiguration(classes = SftpTestConfig .class)
class ExampleThreeIT {
#Autowired
private EmbeddedSftpServer embeddedSftpServer;
}
And i run all the test classes simultaneously, i found out that for the test classes annotated with #ExtendWith(SpringExtension.class), it's the same context that is used (which is understandable since i guess spring cache it) and therefore the bean lifecycle methods are not executed again, but to my surprise, for the class annotated with #SpringBatchTest i noticed that the life cycle hooks of the bean are executed again! Which is a behavior that is not convenient since i want the application context to start the server one time for all tests and close it at the end of those tests (which is the case if i use only #ExtendWith(SpringExtension.class) for all my test classes).
N.B. : I need to use #SpringBachTest for my ExampleThreeIT test class.
I think you are hitting this issue: https://github.com/spring-projects/spring-batch/issues/3940 which has been fixed in v4.3.4/4.2.8. Upgrading to one of these versions should fix your issue.

Spring Boot - Testing - tearDown for a bean

I use #EmbeddedKafka annotation as follows to have a kafka mock:
#ExtendWith(SpringExtension.class)
#SpringBootTest
#EmbeddedKafka(partitions = 1,
topics = {"topic"},
brokerProperties = {
"auto.create.topics.enable=${topics.autoCreate:false}",
"delete.topic.enable=${topic.delete:true}",
"broker.id=2"})
public class KafkaUsersTest {
#Autowired
private EmbeddedKafkaBroker embeddedKafka;
#Test
public void test1() {
// test something
}
#Test
public void test2() {
// test something
}
...
}
Now, after The tests finish I'd like to close the embeddedKafka bean. Something like this:
#AfterAll
public void tearDown(){
embeddedKafka.getKafkaServers().forEach(KafkaServer::shutdown);
embeddedKafka.getKafkaServers().forEach(KafkaServer::awaitShutdown);
}
The problem is:
An #AfterAll method can only be static.
If I make it static - then also the embeddedKafka has to be static, and then #Autowired annotation will not work.
I guess I can the bean to a static field from one of the tests and then use it in the tearDown(), but it's really ugly.
What is the "good practice" for closing a bean only once after all tests have completed?
An #AfterAll method can only be static.
That's not true.
From the JUnit 5 User Guide:
Denotes that the annotated method should be executed after all #Test, #RepeatedTest, #ParameterizedTest, and #TestFactory methods in the current class; analogous to JUnit 4’s #AfterClass. Such methods are inherited (unless they are hidden or overridden) and must be static (unless the "per-class" test instance lifecycle is used).
An #AfterAll method can be non-static if you use #TestInstance(Lifecycle.PER_CLASS). This is also documented in the JUnit 5 User Guide:
The "per-class" mode has some additional benefits over the default "per-method" mode. Specifically, with the "per-class" mode it becomes possible to declare #BeforeAll and #AfterAll on non-static methods as well as on interface default methods.

Junit set system property for test not working

I have an issue that I would have thought I could resolve by now...
I'm writing a few simple tests to hit a couple services...
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class EndpointTests {
private static Logger log = Logger.getLogger(ApplicationController.class.getName());
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#Mock
private ApplicationController applicationController = new ApplicationController();
static {
System.setProperty("audit.enabled", "false");
}
#BeforeClass
public static void setupProperties() {
System.setProperty("audit.enabled", "false");
}
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
#Test
public void contextLoads() {}
#Test
public void testGetApplications() throws Exception {
mockMvc.perform(get("/applications/")).andExpect(status().isOk())
.andDo(print())
.andExpect(content().contentType(TestUtils.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.[0].name",is("XYZ")));
}
Long story short, I need this property disabled when running tests. I've tried setting the property in a static initializer and in #BeforeClass as I've seen on other posts but when it goes into the actual method, it's still its default 'enabled' value and the tests fail. I'm not using XML configuration so would prefer a code/annotation solution.
Any suggestions on another way I can fix this? Thanks.
UPDATE
Seems like every time my integration test runs:
#Test
public void testGetApplications() throws Exception {
mockMvc.perform(get("/applications/")).andExpect(status().isOk())
.andDo(print())
.andExpect(content().contentType(TestUtils.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.[0].name",is("paasport-services")));
}
It executes my #Configuration classes on the call to mockMvc.perform...
#Configuration
public class AppConfiguration
...
...
So setting the property value in my test class does no good.
Is there any way to get in between and set this one property for my tests? I don't want to really create a separate test application context as it's just one property and everything has been working well for me up to this point.
Thanks.
I'm sure there's a much more elegant solution but I simply set a new system property in #BeforeClass on my test class.
My audit is handled by an aspect and I simply check that property I set only in my test class. If it's set to true, the advice doesn't execute.

Mock an Autowired ExecutorService

Abstract:
I have a Spring #Component that uses an autowired ExecutorService as a work pool. I'm using JUnit and Mockito to test the functionality of the component and I need to mock that Executor Service. This has been trivial for other autowired members - a generic helper, and a DAO layer for instance are easily mocked, but I need a real Executor Service.
Code:
#RunWith(MockitoJUnitRunner.class)
public class MadeUpClassNameTest{
#Mock
private ExecutorService executor;
#Before
public void initExecutor() throws Exception{
executor = Executors.newFixedThreadPool(2);
}
#InjectMocks
private ASDF componentBeingAutowired;
...
This alone doesn't work, the results of invokeAll() is always an empty list.
Attempting to more explicitly mock the executor method also doesn't work...
#Test
public void myTestMethod(){
when(executor.invokeAll(anyCollection()))
.thenCallRealMethod();
...
}
I get the cryptically worded exception:
You cannot use argument matchers outside of verification or stubbing.
(I thought this was a stubbing ?)
I could provide a thenReturn(Answer<>) method, but I'd like to make sure that the code actually works with an executor, a fair bit of the code is devoted to mapping the results of Futures.
Problem
How do I provide a real (or functionally usable mock) Executor Service ? Alternatively, is my difficulty in testing this component a sign that this is a poor design in need of refactoring, or possibly a bad test scenario ?
Notes
I want to stress that my problem is NOT getting Mockito or Junit set up. Other mocks and tests work correctly. My problem is specific only to the particular mock above.
Using: Junit 4.12, Mockito 1.10.19, Hamcrest 1.3
I think the following code runs after the Mock is injected.
#Before
public void initExecutor() throws Exception{
executor = Executors.newFixedThreadPool(2);
}
This causes your local copy of executor to be set, but not the one that is injected.
I would recommend using constructor injection in on your componentBeingAutowired and create a new one in your unit test and exclude Spring dependencies. Your test could then look like something below:
public class MadeUpClassNameTest {
private ExecutorService executor;
#Before
public void initExecutor() throws Exception {
executor = Executors.newFixedThreadPool(2);
}
#Test
public void test() {
ASDF componentBeingTested = new ASDF(executor);
... do tests
}
}
Another way is to use ReflectionTestUtils to inject the executor
#Before
public void initExecutor() {
ReflectionTestUtils.setField(componentBeingAutowired, "executor", Executors.newFixedThreadPool(2);
}
You can use the #Spy annotation.
#RunWith(MockitoJUnitRunner.class)
public class MadeUpClassNameTest{
#Spy
private final ExecutorService executor = Executors.newFixedThreadPool(2);
....
}
You can use the #Spy annotation.
#RunWith(MockitoJUnitRunner.class)
public class MadeUpClassNameTest{
#Spy
private final ExecutorService executor = Executors.newFixedThreadPool(2);
#Test
...
}

Spring test #ContextConfiguration and static context

I have the following piece of code for my abstract test class (I know XmlBeanFactory with ClassPathResource is deprecated, but it's unlikely to be the case of the problem).
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public abstract class AbstractIntegrationTest {
/** Spring context. */
protected static final BeanFactory context = new XmlBeanFactory(new ClassPathResource(
"com/.../AbstractIntegrationTest-context.xml"));
...
}
It loads the default test configuration XML file AbstractIntegrationTest-context.xml (and then I use autowiring). I also need to use Spring in static methods annotated with #BeforeClass and #AfterClass, so I have a separate context variable pointing to the same location. But the thing is that this is a separate context, which will have different instances of beans. So how can I merge these contexts or how can I invoke Spring's bean initialization defined by #ContextConfiguration from my static context?
I have in mind a possible solution by getting rid of those static members, but I'm curious, if I can do it with relatively small changes to the code.
You are right, your code will produce two application contexts: one will be started, cached and maintained for you by #ContextConfiguration annotation. The second context you create yourself. It doesn't make much sense to have both.
Unfortunately JUnit is not very well suited for integration tests - mainly because you cannot have before class and after class non-static methods. I see two choices for you:
switch to testng - I know it's a big step
encode your setup/tear down logic in a Spring bean included in the context only during tests - but then it will run only once, before all tests.
There are also less elegant approaches. You can use static variable and inject context to it:
private static ApplicationContext context;
#AfterClass
public static afterClass() {
//here context is accessible
}
#Autowired
public void setApplicationContext(ApplicationContext applicationContext) {
context = applicationContext;
}
Or you can annotate your test class with #DirtiesContext and do the cleanup in some test bean:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#DirtiesContext(classMode = AFTER_CLASS)
public abstract class AbstractIntegrationTest {
//...
}
public class OnlyForTestsBean {
#PreDestroy
public void willBeCalledAfterEachTestClassDuringShutdown() {
//..
}
}
Not sure whether you chose any approach here, but I encounter the same problem and solved it another way using Spring test framework's TestExecutionListener.
There are beforeTestClass and afterTestClass, so both equivalent to #BeforeClass and #AfterClass in JUnit.
The way I do it:
#RunWith(SpringJUnit4ClassRunner.class)
#TestExecutionListeners(Cleanup.class)
#ContextConfiguration(locations = { "/integrationtest/rest_test_app_ctx.xml" })
public abstract class AbstractIntegrationTest {
// Start server for integration test.
}
You need to create a class that extends AbstractTestExecutionListener:
public class Cleanup extends AbstractTestExecutionListener
{
#Override
public void afterTestClass(TestContext testContext) throws Exception
{
System.out.println("cleaning up now");
DomainService domainService=(DomainService)testContext.getApplicationContext().getBean("domainService");
domainService.delete();
}
}
By doing this, you have access to the application context and do your setup/teardown here with spring beans.
Hopefully this help anyone trying to do integration test like me using JUnit + Spring.

Resources