From 9259fa6ca9f75582bd4251b9d7161e27ba78f54b Mon Sep 17 00:00:00 2001 From: root Date: Mon, 2 Feb 2026 15:34:37 +0100 Subject: [PATCH 1/2] Add unit and integration tests --- .../config/RabbitConfigurationTest.java | 37 ++++++++++++++ .../producer/HealthRequestProducerTest.java | 30 ++++++++++++ .../web/RequestProducerControllerIT.java | 49 +++++++++++++++++++ .../web/RequestProducerControllerTest.java | 32 ++++++++++++ .../web/dto/MessageRequestValidationTest.java | 22 +++++++++ 5 files changed, 170 insertions(+) create mode 100644 src/test/java/de/dev089/eventproducer/config/RabbitConfigurationTest.java create mode 100644 src/test/java/de/dev089/eventproducer/producer/HealthRequestProducerTest.java create mode 100644 src/test/java/de/dev089/eventproducer/web/RequestProducerControllerIT.java create mode 100644 src/test/java/de/dev089/eventproducer/web/RequestProducerControllerTest.java create mode 100644 src/test/java/de/dev089/eventproducer/web/dto/MessageRequestValidationTest.java diff --git a/src/test/java/de/dev089/eventproducer/config/RabbitConfigurationTest.java b/src/test/java/de/dev089/eventproducer/config/RabbitConfigurationTest.java new file mode 100644 index 0000000..0af65d0 --- /dev/null +++ b/src/test/java/de/dev089/eventproducer/config/RabbitConfigurationTest.java @@ -0,0 +1,37 @@ +package de.dev089.eventproducer.config; + +import org.junit.jupiter.api.Test; +import org.springframework.amqp.core.Binding; +import org.springframework.amqp.core.Queue; +import org.springframework.amqp.core.TopicExchange; +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.support.converter.MessageConverter; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +class RabbitConfigurationTest { + + @Test + void beans_areConfiguredFromProperties() { + RabbitConfiguration config = new RabbitConfiguration(); + ReflectionTestUtils.setField(config, "producerExchange", "exchange.x"); + ReflectionTestUtils.setField(config, "queueName", "queue.q"); + ReflectionTestUtils.setField(config, "routingKey", "route.r"); + + TopicExchange exchange = config.topicExchange(); + Queue queue = config.producerQueue(); + Binding binding = config.queueBinding(queue, exchange); + MessageConverter converter = config.jacksonConverter(); + RabbitTemplate template = config.rabbitTemplate(mock(ConnectionFactory.class), converter); + + assertThat(exchange.getName()).isEqualTo("exchange.x"); + assertThat(exchange.isDurable()).isTrue(); + assertThat(queue.getName()).isEqualTo("queue.q"); + assertThat(binding.getRoutingKey()).isEqualTo("route.r"); + assertThat(template.getExchange()).isEqualTo("exchange.x"); + assertThat(template.getMessageConverter()).isSameAs(converter); + } +} diff --git a/src/test/java/de/dev089/eventproducer/producer/HealthRequestProducerTest.java b/src/test/java/de/dev089/eventproducer/producer/HealthRequestProducerTest.java new file mode 100644 index 0000000..e341ff0 --- /dev/null +++ b/src/test/java/de/dev089/eventproducer/producer/HealthRequestProducerTest.java @@ -0,0 +1,30 @@ +package de.dev089.eventproducer.producer; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.amqp.rabbit.core.RabbitTemplate; + +import static org.mockito.Mockito.verify; + +class HealthRequestProducerTest { + + @Test + void publish_usesProvidedRoutingKey() { + RabbitTemplate rabbitTemplate = Mockito.mock(RabbitTemplate.class); + HealthRequestProducer producer = new HealthRequestProducer(rabbitTemplate, "default.route"); + + producer.publish("custom.route", "payload"); + + verify(rabbitTemplate).convertAndSend("custom.route", "payload"); + } + + @Test + void publishToDefaultQueue_usesDefaultRoutingKey() { + RabbitTemplate rabbitTemplate = Mockito.mock(RabbitTemplate.class); + HealthRequestProducer producer = new HealthRequestProducer(rabbitTemplate, "default.route"); + + producer.publishToDefaultQueue("payload"); + + verify(rabbitTemplate).convertAndSend("default.route", "payload"); + } +} diff --git a/src/test/java/de/dev089/eventproducer/web/RequestProducerControllerIT.java b/src/test/java/de/dev089/eventproducer/web/RequestProducerControllerIT.java new file mode 100644 index 0000000..abe48eb --- /dev/null +++ b/src/test/java/de/dev089/eventproducer/web/RequestProducerControllerIT.java @@ -0,0 +1,49 @@ +package de.dev089.eventproducer.web; + +import de.dev089.eventproducer.producer.HealthRequestProducer; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest(properties = { + "RABBITMQ_HOST=localhost", + "RABBITMQ_PORT=5672", + "RABBITMQ_USERNAME=guest", + "RABBITMQ_PASSWORD=guest" +}) +@AutoConfigureMockMvc +class RequestProducerControllerIT { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private HealthRequestProducer healthRequestProducer; + + @Test + void postPopulate_acceptsValidRequest() throws Exception { + mockMvc.perform(post("/api/events/populate") + .contentType(MediaType.APPLICATION_JSON) + .content("{\"message\":\"hello\"}")) + .andExpect(status().isAccepted()); + + verify(healthRequestProducer).publishToDefaultQueue(any()); + } + + @Test + void postPopulate_rejectsInvalidRequest() throws Exception { + mockMvc.perform(post("/api/events/populate") + .contentType(MediaType.APPLICATION_JSON) + .content("{\"message\":\"\"}")) + .andExpect(status().isBadRequest()); + } +} diff --git a/src/test/java/de/dev089/eventproducer/web/RequestProducerControllerTest.java b/src/test/java/de/dev089/eventproducer/web/RequestProducerControllerTest.java new file mode 100644 index 0000000..30ffc83 --- /dev/null +++ b/src/test/java/de/dev089/eventproducer/web/RequestProducerControllerTest.java @@ -0,0 +1,32 @@ +package de.dev089.eventproducer.web; + +import de.dev089.eventproducer.producer.HealthRequestProducer; +import de.dev089.eventproducer.web.dto.MessageRequest; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; + +import java.time.Instant; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.verify; + +class RequestProducerControllerTest { + + @Test + void publishMessage_wrapsMessageAndTimestamp() { + HealthRequestProducer producer = Mockito.mock(HealthRequestProducer.class); + RequestProducerController controller = new RequestProducerController(producer); + + controller.publishMessage(new MessageRequest("hello")); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(Map.class); + verify(producer).publishToDefaultQueue(captor.capture()); + + Map envelope = captor.getValue(); + assertThat(envelope.get("message")).isEqualTo("hello"); + assertThat(envelope.get("sentAt")).isInstanceOf(String.class); + assertThat(Instant.parse((String) envelope.get("sentAt"))).isNotNull(); + } +} diff --git a/src/test/java/de/dev089/eventproducer/web/dto/MessageRequestValidationTest.java b/src/test/java/de/dev089/eventproducer/web/dto/MessageRequestValidationTest.java new file mode 100644 index 0000000..85b33ff --- /dev/null +++ b/src/test/java/de/dev089/eventproducer/web/dto/MessageRequestValidationTest.java @@ -0,0 +1,22 @@ +package de.dev089.eventproducer.web.dto; + +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; + +class MessageRequestValidationTest { + + private final Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); + + @Test + void message_mustNotBeBlank() { + Set> violations = validator.validate(new MessageRequest("")); + + assertThat(violations).isNotEmpty(); + } +} -- 2.52.0 From 4d794afc7d6353880bb3ee967a71b41f6f7d998c Mon Sep 17 00:00:00 2001 From: root Date: Mon, 2 Feb 2026 15:47:40 +0100 Subject: [PATCH 2/2] Fix tests for Spring Boot 4 and test config --- build.gradle | 2 ++ .../EventProducerApplicationTests.java | 7 ++++- .../web/RequestProducerControllerIT.java | 30 +++++++++++++++---- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index 43997cc..9df8580 100644 --- a/build.gradle +++ b/build.gradle @@ -26,6 +26,8 @@ dependencies { implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0' testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.springframework.boot:spring-boot-test' + testImplementation 'org.springframework.boot:spring-boot-test-autoconfigure' } tasks.named('test') { diff --git a/src/test/java/de/dev089/eventproducer/EventProducerApplicationTests.java b/src/test/java/de/dev089/eventproducer/EventProducerApplicationTests.java index 4f07b79..408d850 100644 --- a/src/test/java/de/dev089/eventproducer/EventProducerApplicationTests.java +++ b/src/test/java/de/dev089/eventproducer/EventProducerApplicationTests.java @@ -3,7 +3,12 @@ package de.dev089.eventproducer; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; -@SpringBootTest +@SpringBootTest(properties = { + "RABBITMQ_HOST=localhost", + "RABBITMQ_PORT=5672", + "RABBITMQ_USERNAME=guest", + "RABBITMQ_PASSWORD=guest" +}) class EventProducerApplicationTests { @Test diff --git a/src/test/java/de/dev089/eventproducer/web/RequestProducerControllerIT.java b/src/test/java/de/dev089/eventproducer/web/RequestProducerControllerIT.java index abe48eb..56ab71c 100644 --- a/src/test/java/de/dev089/eventproducer/web/RequestProducerControllerIT.java +++ b/src/test/java/de/dev089/eventproducer/web/RequestProducerControllerIT.java @@ -1,13 +1,18 @@ package de.dev089.eventproducer.web; import de.dev089.eventproducer.producer.HealthRequestProducer; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; +import org.springframework.boot.test.context.TestConfiguration; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; @@ -20,14 +25,29 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. "RABBITMQ_USERNAME=guest", "RABBITMQ_PASSWORD=guest" }) -@AutoConfigureMockMvc class RequestProducerControllerIT { @Autowired + private WebApplicationContext context; + + @Autowired + private HealthRequestProducer healthRequestProducer; + private MockMvc mockMvc; - @MockBean - private HealthRequestProducer healthRequestProducer; + @BeforeEach + void setUp() { + this.mockMvc = MockMvcBuilders.webAppContextSetup(context).build(); + } + + @TestConfiguration + static class TestConfig { + @Bean + @Primary + HealthRequestProducer healthRequestProducer() { + return Mockito.mock(HealthRequestProducer.class); + } + } @Test void postPopulate_acceptsValidRequest() throws Exception { -- 2.52.0