Spring Boot, test main application class - java

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

Related

Running TestContainer once for each Unit/Integration test in JUnit5 with Micronaut application

I am using a Test container with JUnit 5 in Micronaut application. I have many integration tests and I want to create and run the Test container once for each integration test.
**1st test on different file **
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#MicronautTest
public class DiscountGetListenerTest extends TestContainerFixture {
#Inject
static IDiscountProducer iDiscountProducer;
#Test
#DisplayName("Should return the discount based on the specified id")
void shouldReturnTheDiscountBasedOnTheSpecifiedId() {
}
}
2nd test on different file
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#MicronautTest
public class DiscountListenerTest extends TestContainerFixture {
#Inject
IDiscountProducer iDiscountProducer;
#Test
#DisplayName("Should return discounts list")
void shouldReturnDiscountsList() {
var item = iDiscountProducer.Find(new DiscountCriteriaCommand(null)).blockingGet();
assertTrue(item.size() == 0);
}
}
Test container fixture
#Testcontainers
public class TestContainerFixture {
#Container
public GenericContainer mongoDBContainer = new GenericContainer(DockerImageName.parse("mongo:4.0.10"))
.withExposedPorts(27017);
#Container
public GenericContainer rabbitMQContainer = new GenericContainer(DockerImageName.parse("rabbitmq:3-management-alpine"))
.withExposedPorts(5672);
}
When I run the application on each Integration test the container is created and started, however, I want to create once and start the container once
For example
On each test case the container is created and staring... How can I create and start the container once ??
Maybe you can try singleton container pattern explained in here: https://www.testcontainers.org/test_framework_integration/manual_lifecycle_control/
So for your code :
public abstract class TestContainerFixture {
public static final GenericContainer mongoDBContainer;
public static final GenericContainer rabbitMQContainer;
static {
mongoDBContainer = new GenericContainer(DockerImageName.parse("mongo:4.0.10"))
.withExposedPorts(27017);
rabbitMQContainer = new GenericContainer(DockerImageName.parse("rabbitmq:3-management-alpine"))
.withExposedPorts(5672);
mongoDbContainer.start();
rabbitMQContainer.start();
}
}
And when you extend this abstract class in your test, maybe that would work.

Spring dynamic #Bean registration with SpringApplicationBuilder failing in test

Trying to register beans dynamically via SpringApplicationBuilder class and it's working when running the app, but when trying to execute the test and trying to verify that the beans are defined in the context, they fail for the dynamic bean. Feel like I have to use another "magical" annotation for the tests for them to properly load the dynamic beans.
This is the code used and if you run the tests you will see that both cases will fail. BarService will fail also because FooService is registered dynamically via builder, but if you would remove the dependency it will pass the BarService test.
SpringApp.java
class FooService {
}
#Component
class BarService {
private final FooService fooService;
BarService(FooService fooService) {
this.fooService = fooService;
}
}
#SpringBootApplication
public class SpringApp {
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(SpringApp.class)
.initializers((ApplicationContextInitializer<GenericApplicationContext>) context -> {
context.registerBean(FooService.class);
})
.run(args);
}
}
SpringAppTest.java
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = SpringApp.class)
public class SpringAppTest {
#Autowired
ApplicationContext context;
#Test
public void barService() {
Assert.assertNotNull("The barService should not be null", context.getBean(BarService.class));
}
#Test
public void contextLoads() {
Assert.assertNotNull("The fooService should not be null", context.getBean(FooService.class));
}
}
First solution
The main error here is that the test does not have the initalization logic that the main method has. Solution is to extract the logic from the initializers method
class MyInitializer implements ApplicationContextInitializer<GenericApplicationContext> {
#Override
public void initialize(GenericApplicationContext context) {
System.out.println("Called initialize");
context.registerBean(FooService.class);
}
}
and use it in main
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(SpringApp.class)
.initializers(new MyInitializer())
.run(args);
}
and then use the MyInitializer in the test file through #ConextConfiguration
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = SpringApp.class, initializers = MyInitializer.class)
public class SpringAppTest {
// ...
}
Second (better) solution
Now, this can be cumbersome as we need to reference this initializer in every test, but there is an even better solution. We can create a specific Spring file resources/META-INF/spring.factories and put inside of it a reference to the initializer:
org.springframework.context.ApplicationContextInitializer=com.acme.orders.MyInitializer
After that, we can simplify both the main method
#SpringBootApplication
public class SpringApp {
public static void main(String[] args) {
SpringApplication.run(SpringApp.class, args);
}
}
and the tests, so that they don't need to always import the initializer.
#RunWith(SpringRunner.class)
#SpringBootTest
public class SpringAppTest {
// ...
}
Now both the main run process and the tests will have access to all the beans.

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.

How to call tests from another test?

How can I call tests from another test?
I have a class in the jar that I have added as dependency into my project:
public class Tests{
private MockMvc mockMvc;
#Test
public void test1() throws Exception {
.....
mockMvc.perform(get(myRequest)
.content(dataFromDB)
.......
}
}
#Test
public void test2() throws Exception {
.....
mockMvc.perform(get(myRequest)
.content(dataFromDB)
.......
}
}
.......
And in my project I have:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = MyApp.class)
public class MyTests {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext context;
#Before
public void init() throws Exception {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build();
}
#Test
public void test() throws Exception {
CALL SOMEHOW TESTS FROM THE JAR HERE
}
I want those tests from the jar to test my project's database (for example: dataFromDB should be some data from the project where this dependency has been added).
I have already added this jar and I can call class Tests inside my project,so I have access to it. I am just not sure how to run those tests inside it.
What should I change so it works well? Thanks
Updated:
*I want all tests from the jar to call at the same time, not individually.
*I want to give jar access to my db, so it can get all needed testing data in the db table of my project.
From what is see, you have 2 sets of environment, and 1 set of tests.
So one way to solve this is that you make the environment passable, the mockmvc, the dataFromDb, etc, so that the tests can execute independently of the environment.
I would suggest having the test methods in another class, like this very simplified example for easy reading:
class MyTestMethods {
void test1(TestEnv env, Req myRequest) {
env.getMockMvc()
.perform(env.get(myRequest)
.content(env.getDataFromDB());
// assertions here
}
}
class OldTestInJar {
#Test
public void test1() {
new MyTestMethods().test1(myEnv, myReq);
}
}
class MyNewTest {
#Test
public void test1() {
new MyTestMethods().test1(myNewEnv, myNewReq);
}
}

Run External Command Before JUnit Tests in Eclipse

Is it possible to run an external command before running tests in a given JUnit file? I run my tests using the Eclipse's Run command. Using JUnit 4.
Thanks.
Very vague question. Specifically, you didn't mention how you are running your JUnit tests. Also you mentioned 'file', and a file can contain several JUnit tests. Do you want to run the external command before each of those tests, or before any of them are executed?
But more on topic:
If you are using JUnit 4 or greater then you can tag a method with the #Before annotation and the method will be executed before each of your tagged #Test methods. Alternatively, tagging a static void method with #BeforeClass will cause it to be run before any of the #Test methods in the class are run.
public class MyTestClass {
#BeforeClass
public static void calledBeforeAnyTestIsRun() {
// Do something
}
#Before
public void calledBeforeEachTest() {
// Do something
}
#Test
public void testAccountCRUD() throws Exception {
}
}
If you are using a version of JUnit earlier than 4, then you can override the setUp() and setUpBeforeClass() methods as replacements for #Before and #BeforeClass.
public class MyTestClass extends TestCase {
public static void setUpBeforeClass() {
// Do something
}
public void setUp() {
// Do something
}
public void testAccountCRUD() throws Exception {
}
}
Assuming you are using JUnit 4.0, you could do the following:
#Test
public void shouldDoStuff(){
Process p = Runtime.getRuntime().exec("application agrument");
// Run the rest of the unit test...
}
If you want to run the external command for every unit test, then you should do it in the #Before setup method.

Categories

Resources