Spring boot test - wait for termination - java

I have an application written with Spring boot and I am trying to write some integration tests for it. I want to run my spring boot application and wait until it terminates so I can assert some state.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(MyApp.class)
#IntegrationTest("initParams")
public class Test {
#Test
public void test() {
// need to wait until termination
}
Is there any way I can wait for the termination of the Spring app without modifying the application code?

If you the test needs the app to terminates, it would be better to actually run the app as part of the test rather than letting the test context starts it for you. I am not sure why you need it and I wouldn't recommend it for all use cases but this could work:
public class Test {
#Test
public void test() {
String[] args = new String[]{"initParams"}; // To adapt
ConfigurableApplicationContext ctx = SpringApplication.run(MyApp.class, args);
// whatever
ctx.close();
// whatever after the app has terminated.
}
}

I'm not sure if that is a good idea, but here it is: You could perform some asserts in an #After and #AfterClass methods ?
#After
public void afterMethods() {
//asserts
}
#AfterClass
public static void afterClass() {
//asserts
}

Related

How to boot an app in spring integration test?

I need to create an integration test for my app. I used #SpringBootTest(classes = {Application.class}) annotation to boot it, but its launch takes time. So how can i run test, when my app is ready?
The problem is in kafka listener:
#SpringBootApplication
public class Application {
#Autowired
private KafkaConsumeHandler kafkaConsumeHandler;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#KafkaListener(topics = "${kafka.topics.test}", containerFactory = "kafkaListenerContainerFactory")
public void listenRegistred(KafkaMessage consumeKafka) {
kafkaConsumeHandler.handleStartProcess(consumeKafka);
}
If i try to send messages immediately in test, the listener cant catch them. So i used a little pause before sending.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {Application.class})
#DirtiesContext
public class ProcessTest {
#ClassRule
public static KafkaEmbedded embeddedKafka = new KafkaEmbedded(1, true, "testTopic");
#Test
public void sendTestRegistred() throws Exception {
Thread.sleep(5000); // Need a delay to boot an application
...
}
You need to add the class that is annotated with #SpringBootApplication.
Example:
#SpringBootApplication
public class SpringApp {}
#SpringBootTest(classes = SpringApp.class)
public class IntegrationTest {}
Also, note that the integration test will always be slower than unit test and you need to determinate what kind of test you need to test a certain functionality.
Update after updates in question:
In your case, the delay of the test is caused because of waiting on KafkaEmbded to start. So you have to find a way programmatically to determinate when Kafka is ready. This is one possibility that should work:
#Before
public void setUp() throws Exception {
// wait until the partitions are assigned
for (MessageListenerContainer messageListenerContainer :
kafkaListenerEndpointRegistry.getListenerContainers()) {
ContainerTestUtils.waitForAssignment(messageListenerContainer,
embeddedKafka.getPartitionsPerTopic());
}
Code is taken from here: https://github.com/code-not-found/spring-kafka/blob/master/spring-kafka-avro/src/test/java/com/codenotfound/kafka/SpringKafkaApplicationTest.java#L42
If this doesn't work look for how to wait on KafkaEmbedded start-up. Your problem is not caused by SpringBootTest.

Spring Boot, test main application class

I have a logic in my application class in a Spring Boot app, but I don't know how to do an unit and an integration test to cover it.
Here is the code.
#SpringBootApplication
public class MlgApplication {
public static void main(String[] args) throws IOException {
ConfigurableApplicationContext run = SpringApplication.run(MlgApplication.class, args);
ListBean ListBean = run.getBean(ListBean.class);
ListBean.createList();
}
}
It's a command line application that runs with 'java -jar mlg.jar'
If you are using Spring initializr, this test will be created for you. You may call it an integration test because it will try to start your application context (thus integrating all classes inisde it). It goes something like this:
#RunWith(SpringRunner.class)
#SpringBootTest
public class BootApplicationTests {
#Test
public void contextLoads() {
// some more optional integration assertions here
// like asserting number of beans, are they null, etc...
}
}
And for your specific domain logic, you can try to assert if the list is created but I would put that in a separate class as a unit test.
I managed to do it this way, using mockito-core 3.8 and mockito-inline BUT was not able to get Jacoco coverage doing it this way:
#SpringBootTest
#ActiveProfiles("test")
public class AutowireTest {
private static final String ARG = "";
private static final String[] ARGS = new String[]{ARG};
#Autowired
ConfigurableApplicationContext context;
#Test //Junit5
public void main() {
try (MockedStatic<Application> appStatic = Mockito.mockStatic(Application.class);
MockedStatic<SpringApplication> springStatic = Mockito.mockStatic(
SpringApplication.class)) {
appStatic.when(() -> Application.main(ARGS))
.thenCallRealMethod();
springStatic.when(() -> SpringApplication.run(Application.class, ARGS))
.thenReturn(context);
// when
Application.main(ARGS);
//then
appStatic.verify(times(1),
() -> Application.main(ARGS));
springStatic.verify(times(1),
() -> SpringApplication.run(Application.class, ARGS));
}
}
}
So, I am asking why here: How to Unit test Spring-Boot Application main() method to get Jacoco test coverage

Spring-boot stop application when internal error occurs [duplicate]

How can I programmatically shutdown a Spring Boot application without terminating the VM?
In other works, what is the opposite of
new SpringApplication(Main.class).run(args);
Closing a SpringApplication basically means closing the underlying ApplicationContext. The SpringApplication#run(String...) method gives you that ApplicationContext as a ConfigurableApplicationContext. You can then close() it yourself.
For example,
#SpringBootApplication
public class Example {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(Example.class, args);
// ...determine it's time to shut down...
ctx.close();
}
}
Alternatively, you can use the static SpringApplication.exit(ApplicationContext, ExitCodeGenerator...) helper method to do it for you. For example,
#SpringBootApplication
public class Example {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(Example.class, args);
// ...determine it's time to stop...
int exitCode = SpringApplication.exit(ctx, new ExitCodeGenerator() {
#Override
public int getExitCode() {
// no errors
return 0;
}
});
// or shortened to
// int exitCode = SpringApplication.exit(ctx, () -> 0);
System.exit(exitCode);
}
}
The simplest way would be to inject the following object where you need to initiate the shutdown
ShutdownManager.java
import org.springframework.context.ApplicationContext;
import org.springframework.boot.SpringApplication;
#Component
class ShutdownManager {
#Autowired
private ApplicationContext appContext;
/*
* Invoke with `0` to indicate no error or different code to indicate
* abnormal exit. es: shutdownManager.initiateShutdown(0);
**/
public void initiateShutdown(int returnCode){
SpringApplication.exit(appContext, () -> returnCode);
}
}
This works, even done is printed.
SpringApplication.run(MyApplication.class, args).close();
System.out.println("done");
So adding .close() after run()
Explanation:
public ConfigurableApplicationContext run(String... args)
Run the Spring application, creating and refreshing a new
ApplicationContext. Parameters:
args - the application arguments (usually passed from a Java main
method)
Returns:
a running ApplicationContext
and:
void close() Close this application context, releasing all resources
and locks that the implementation might hold. This includes destroying
all cached singleton beans. Note: Does not invoke close on a parent
context; parent contexts have their own, independent lifecycle.
This method can be called multiple times without side effects:
Subsequent close calls on an already closed context will be ignored.
So basically, it will not close the parent context, that's why the VM doesn't quit.
This will make sure that the SpringBoot application is closed properly and the resources are released back to the operating system,
#Autowired
private ApplicationContext context;
#GetMapping("/shutdown-app")
public void shutdownApp() {
int exitCode = SpringApplication.exit(context, (ExitCodeGenerator) () -> 0);
System.exit(exitCode);
}
In the application you can use SpringApplication. This has a static exit() method that takes two arguments: the ApplicationContext and an ExitCodeGenerator:
i.e. you can declare this method:
#Autowired
public void shutDown(ExecutorServiceExitCodeGenerator exitCodeGenerator) {
SpringApplication.exit(applicationContext, exitCodeGenerator);
}
Inside the Integration tests you can achieved it by adding #DirtiesContext annotation at class level:
#DirtiesContext(classMode=ClassMode.AFTER_CLASS) - The associated ApplicationContext will be marked as dirty after the test class.
#DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD) - The associated ApplicationContext will be marked as dirty after each test method in the class.
i.e.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = {Application.class},
webEnvironment= SpringBootTest.WebEnvironment.DEFINED_PORT, properties = {"server.port:0"})
#DirtiesContext(classMode= DirtiesContext.ClassMode.AFTER_CLASS)
public class ApplicationIT {
...

How to release the port number after every JUnit test in spring boot application?

I have a JUnit test class written in spring boot which contains 3 unit tests:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class)
#ContextConfiguration
#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public class AppclaitionTest {
#BeforeClass
public static void initialize(){
System.out.println(">>>>>>>Intialization Part!");
}
#Test
public void test1(){
System.out.println(">>>>>>>Appcliation Test Started! - Test 1");
Application.main(new String[]{});
System.out.println(">>>>>>>Appcliation Test Ended! - Test 1");
}
#Test
public void test2(){
System.out.println(">>>>>>>Appcliation Test Started! - Test 2");
Application.main(new String[]{});
System.out.println(">>>>>>>Appcliation Test Ended! - Test 2");
}
#Test
public void test3(){
System.out.println(">>>>>>>Appcliation Test Started! - Test 3");
Application.main(new String[]{});
System.out.println(">>>>>>>Appcliation Test Ended! - Test 3");
}
#AfterClass
public static void destory(){
System.out.println(">>>>>>>Destroy Part!");
}
}
If run after the first test, i am getting below the exception:
java.net.BindException: Address already in use: bind
though the application context released, it is not releasing the port number, hence i'm getting the above exception.
Is there any way that I can close the port number before each unit test?
You should consider starting your Spring Boot application by using the SpringApplication class rather than invoking directly the main(String[] args) method of your Spring Boot application that doesn't provide an API to terminate it.
With the SpringApplication way, you could get a ConfigurableApplicationContext instance that provides a way to terminate the running application.
For example :
ConfigurableApplicationContext context;
#Before
public void setup{
SpringApplication springApplication = new SpringApplicationBuilder()
.sources(Application.class)
.build();
context = springApplication.run();
}
#After
public void tearDown(){
SpringApplication.exit(context);
}
#Test
public void test1(){
// action
...
// assertion
...
}
However, you should be cautious about this way of doing.
Starting the Spring Boot application is a relatively expensive operation.
Executing it for any method of test will make slower the tests execution.
Unit tests should be fast. Integration tests that are executing on a integration machine may be slower.
You should also consider this problematic.
Use #After and #Before to handle before and after calls for each unit test.
You could be running into the issue because of more than one springboot tests and you may be using something like surefire to executes test classes in parallel.
With Spring boot you can randomize the port in each junit class as defined below. But note this will be at class level and not at test level.
But I don't really see a need at method level. Springboot tests are slow, so I would advise against it.
#SpringBootTest(classes = Application.class,webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

Programmatically shut down Spring Boot application

How can I programmatically shutdown a Spring Boot application without terminating the VM?
In other works, what is the opposite of
new SpringApplication(Main.class).run(args);
Closing a SpringApplication basically means closing the underlying ApplicationContext. The SpringApplication#run(String...) method gives you that ApplicationContext as a ConfigurableApplicationContext. You can then close() it yourself.
For example,
#SpringBootApplication
public class Example {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(Example.class, args);
// ...determine it's time to shut down...
ctx.close();
}
}
Alternatively, you can use the static SpringApplication.exit(ApplicationContext, ExitCodeGenerator...) helper method to do it for you. For example,
#SpringBootApplication
public class Example {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(Example.class, args);
// ...determine it's time to stop...
int exitCode = SpringApplication.exit(ctx, new ExitCodeGenerator() {
#Override
public int getExitCode() {
// no errors
return 0;
}
});
// or shortened to
// int exitCode = SpringApplication.exit(ctx, () -> 0);
System.exit(exitCode);
}
}
The simplest way would be to inject the following object where you need to initiate the shutdown
ShutdownManager.java
import org.springframework.context.ApplicationContext;
import org.springframework.boot.SpringApplication;
#Component
class ShutdownManager {
#Autowired
private ApplicationContext appContext;
/*
* Invoke with `0` to indicate no error or different code to indicate
* abnormal exit. es: shutdownManager.initiateShutdown(0);
**/
public void initiateShutdown(int returnCode){
SpringApplication.exit(appContext, () -> returnCode);
}
}
This works, even done is printed.
SpringApplication.run(MyApplication.class, args).close();
System.out.println("done");
So adding .close() after run()
Explanation:
public ConfigurableApplicationContext run(String... args)
Run the Spring application, creating and refreshing a new
ApplicationContext. Parameters:
args - the application arguments (usually passed from a Java main
method)
Returns:
a running ApplicationContext
and:
void close() Close this application context, releasing all resources
and locks that the implementation might hold. This includes destroying
all cached singleton beans. Note: Does not invoke close on a parent
context; parent contexts have their own, independent lifecycle.
This method can be called multiple times without side effects:
Subsequent close calls on an already closed context will be ignored.
So basically, it will not close the parent context, that's why the VM doesn't quit.
This will make sure that the SpringBoot application is closed properly and the resources are released back to the operating system,
#Autowired
private ApplicationContext context;
#GetMapping("/shutdown-app")
public void shutdownApp() {
int exitCode = SpringApplication.exit(context, (ExitCodeGenerator) () -> 0);
System.exit(exitCode);
}
In the application you can use SpringApplication. This has a static exit() method that takes two arguments: the ApplicationContext and an ExitCodeGenerator:
i.e. you can declare this method:
#Autowired
public void shutDown(ExecutorServiceExitCodeGenerator exitCodeGenerator) {
SpringApplication.exit(applicationContext, exitCodeGenerator);
}
Inside the Integration tests you can achieved it by adding #DirtiesContext annotation at class level:
#DirtiesContext(classMode=ClassMode.AFTER_CLASS) - The associated ApplicationContext will be marked as dirty after the test class.
#DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD) - The associated ApplicationContext will be marked as dirty after each test method in the class.
i.e.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = {Application.class},
webEnvironment= SpringBootTest.WebEnvironment.DEFINED_PORT, properties = {"server.port:0"})
#DirtiesContext(classMode= DirtiesContext.ClassMode.AFTER_CLASS)
public class ApplicationIT {
...

Categories

Resources