<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Testing Spring Boot Java Applications Cookbook - Medium]]></title>
        <description><![CDATA[Let’s write robust tests in our application. Let’s test each layer of our application properly, by defining goal of a test, tools and mechanisms. - Medium]]></description>
        <link>https://medium.com/testing-spring-boot-java-applications-cookbook?source=rss----c3c96d817911---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Testing Spring Boot Java Applications Cookbook - Medium</title>
            <link>https://medium.com/testing-spring-boot-java-applications-cookbook?source=rss----c3c96d817911---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 22 May 2026 18:48:53 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/testing-spring-boot-java-applications-cookbook" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Testing Security Without Mocked Beans]]></title>
            <link>https://medium.com/testing-spring-boot-java-applications-cookbook/testing-security-without-mocked-beans-91c05aa3281a?source=rss----c3c96d817911---4</link>
            <guid isPermaLink="false">https://medium.com/p/91c05aa3281a</guid>
            <category><![CDATA[spring-test]]></category>
            <category><![CDATA[spring-security]]></category>
            <category><![CDATA[java]]></category>
            <category><![CDATA[spring-boot]]></category>
            <dc:creator><![CDATA[Oleksandr Pohorelov]]></dc:creator>
            <pubDate>Fri, 25 Nov 2022 15:40:27 GMT</pubDate>
            <atom:updated>2023-02-03T08:20:32.246Z</atom:updated>
            <content:encoded><![CDATA[<p>Spring allows using Authentication as Controller’s method argument. For our convenience, it also provides annotation @AuthenticationPrincipal which will extract Principle implementation from SecurityContext. It can be useful when business logic relies on Principal details. Or it can be additional validation of user permissions.</p><h3>Setup test application</h3><p>For example, let’s create the following controller:</p><pre>import com.pohorelov.medium.response.ResourceData;<br>import lombok.RequiredArgsConstructor;<br>import org.springframework.http.HttpStatus;<br>import org.springframework.http.ResponseEntity;<br>import org.springframework.security.core.annotation.AuthenticationPrincipal;<br>import org.springframework.security.oauth2.jwt.Jwt;<br>import org.springframework.web.bind.annotation.GetMapping;<br>import org.springframework.web.bind.annotation.PathVariable;<br>import org.springframework.web.bind.annotation.RestController;<br><br>@RestController<br>@RequiredArgsConstructor<br>public class BaseController {<br><br>  @GetMapping(&quot;/resources/{resourceId}&quot;)<br>  public ResponseEntity&lt;ResourceData&gt; getAccountData(<br>      @AuthenticationPrincipal Jwt principal,<br>      @PathVariable String resourceId<br>  ) {<br>    if (!resourceId.equals(principal.getClaimAsString(&quot;resourceId&quot;))) {<br>      return ResponseEntity<br>          .status(HttpStatus.FORBIDDEN)<br>          .body(AccountData.builder()<br>              .errorMessage(&quot;Search resource id is not associated with current user&quot;)<br>              .build());<br>    }<br>    return ResponseEntity<br>        .status(HttpStatus.OK)<br>        .body(ResourceData.builder()<br>            .resourceId(accountId)<br>            .name(&quot;Oleksandr Pohorelov&quot;)<br>            .build());<br>  }<br><br>}</pre><p>We want to verify that authenticated users asks for their own details, and not for another user’s details. We can do that by extracting resource identifier from JWT token (of course, we assume that our token issuer service is set up to put this id as a claim while generating a new token).</p><p>Of course, this is ultra-simplified example and probably cannot be an example of real production code and logic. However, I am pretty sure that reader of this story has faced some real-life scenarios with similar logic, and that is the reason why you are here.</p><p>Few words about security configuration of our test project. Let’s assume that our organization follows OAuth2 standard and uses Keycloack as a solution. However, this story is not about Keycloack only, its goal is to cover testing of application with any 3rd party identity and access management tool.</p><p>So, our pom.xml contains few dependencies related to security:</p><pre>&lt;dependency&gt;<br>    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;<br>    &lt;artifactId&gt;spring-boot-starter-security&lt;/artifactId&gt;<br>&lt;/dependency&gt;<br><br>&lt;dependency&gt;<br>    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;<br>    &lt;artifactId&gt;spring-boot-starter-oauth2-resource-server&lt;/artifactId&gt;<br>&lt;/dependency&gt;</pre><p>We have basic security configuration as the following:</p><pre>import org.springframework.context.annotation.Bean;<br>import org.springframework.context.annotation.Configuration;<br>import org.springframework.security.config.annotation.web.builders.HttpSecurity;<br>import org.springframework.security.web.SecurityFilterChain;<br><br>@Configuration<br>public class SecurityConfiguration {<br><br>  @Bean<br>  public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {<br>    http<br>        .csrf().disable()<br>        .cors()<br>        .and()<br>        .authorizeRequests()<br>        .anyRequest()<br>        .authenticated()<br>        .and()<br>        .oauth2ResourceServer()<br>        .jwt();<br>    return http.build();<br>  }<br><br>}</pre><p>And to connect to OAuth2 resource we use application properties. For Keycloack JWKS url will look like the following:</p><pre>spring:<br>  security:<br>    oauth2:<br>      resourceserver:<br>        jwt:<br>          jwkSetUri: http://auth-server:9000/auth/realms/myrealm/protocol/openid-connect/certs</pre><p>That’s it. Everything else is Spring Boot magic of auto-configurations. Of course, in real life application we might have more configurations, may custom JwtDecoder or custom provider configuration. We can map claims from token to some POJO to be used as a Principal in an application. Finally, all these logic can be extracted to an internal library, which will be imported by all services in an organization. In fact — more complex logic you have related to security — more useful you will find this story.</p><h3>Controller layer test</h3><p>Developers usually prefer not to mess up with security, and often disable security in WebMvcTests because of that reason. This can be easily done with the following test configuration:</p><pre>@WebMvcTest(controllers = BaseController.class)<br>@AutoConfigureMockMvc(addFilters = false)</pre><p>But this approach brings a lot of limitations:</p><ol><li>What if there is a custom request filter that we would like to test?</li><li>What if we put @PreAutorize annotation on controller class or method and would like to test only users with required roles/permissions can access our API?</li><li>And finally — how to test our controller with @AuthenticationPrincipal?</li></ol><p>Most probably, it is not the best approach to disable security in your tests. Moreover, for the first and second scenarios there are ready-to-go annotations provided by Spring.</p><p>We just need to add test dependency:</p><pre>&lt;dependency&gt;<br>    &lt;groupId&gt;org.springframework.security&lt;/groupId&gt;<br>    &lt;artifactId&gt;spring-security-test&lt;/artifactId&gt;<br>    &lt;scope&gt;test&lt;/scope&gt;<br>&lt;/dependency&gt;</pre><p>And we will be able to use annotations like @WithMockUser to pass Authentication and put requires roles/authorities to SecurityContext.</p><p>However, this still doesn’t address the issue with mocking Principal. We can always fall back to mocking interfaces manually with Mockito. <a href="https://babarowski.com/blog/mock-authentication-with-custom-userdetails/">This article</a> describes this process. Author mocks Authentication, mocks Principal and manually set it to SecurityContext. This approach might be sufficient for your service. But let’s imagine the following scenarios:</p><ul><li>there is custom logic in common organization library related to security you would like to test</li><li>you are writing acceptance or E2E tests that require fully operational application context without any mocked beans</li><li>you DO want to test authentication flow of your service</li></ul><p>Well, to achieve this, let’s first recollect how authorization and authentication with JWT token usually work.</p><h3>Basic overview of authorization and authentication</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KSrCWJdgQedYTc7QpgzpaQ.png" /></figure><p>In simple words, JWT is an “encrypted” JSON. It contains useful data about its expiration time, issuer, it can also contain custom key-value pair (called claims). What makes JWT token secured is signature — made with encryption key using particular algorithm. One of the popular algorithms — <em>RS256 — </em>uses RSA key’s private part. You might be familiar with RSA key concept and might use them already in your developer’s life, for example, to clone git projects with SSH.</p><p>Public part of RSA key is stored along with its identifier in JWKS — JSON Web Key Set. JWKS is managed by 3rd parties (in our case — Keycloack) and used by services with security support to verify incoming JWT token. Security request filters fetch JWT token from request headers, validate expiration date, extract key id and algorithm. Further authentication depends on your configuration, but in our case with OAuth2 and JWKS URL specified in properties — service is going to call 3rd party (Keycloack) to fetch JWKS. Then from this set it gets corresponding public key (by id) and verifies token signature. After successful verification — authentication is completed successfully.</p><p>Considering this schema, we can notice that there is only one part which breaks “fully operational flow” — JWKS provider.</p><h3>Fully operational security with mock server</h3><p>What we can actually do — we can start a mock server, which will return predefined key set. In this key set we will put public part of generated RSA key. With private part of the key we will sign our test JWT with all custom data we want. Exactly as it is working in real life!</p><p>First let’s assure that we have required test dependencies — on Spring Boot starter and mock server:</p><pre>&lt;dependency&gt;<br>    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;<br>    &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;<br>    &lt;scope&gt;test&lt;/scope&gt;<br>&lt;/dependency&gt;<br><br>&lt;dependency&gt;<br>    &lt;groupId&gt;org.mock-server&lt;/groupId&gt;<br>    &lt;artifactId&gt;mockserver-spring-test-listener-no-dependencies&lt;/artifactId&gt;<br>    &lt;version&gt;5.14.0&lt;/version&gt;<br>&lt;/dependency&gt;</pre><p>Now let’s implement test class itself:</p><pre>import static org.hamcrest.Matchers.is;<br>import static org.mockserver.model.HttpRequest.request;<br>import static org.mockserver.model.HttpResponse.response;<br>import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;<br>import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;<br>import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;<br><br>import com.nimbusds.jose.jwk.RSAKey;<br>import com.pohorelov.medium.configuration.SecurityConfiguration;<br>import io.jsonwebtoken.Jwts;<br>import io.jsonwebtoken.SignatureAlgorithm;<br>import java.security.KeyPair;<br>import java.security.KeyPairGenerator;<br>import java.security.interfaces.RSAPublicKey;<br>import java.util.Date;<br>import java.util.List;<br>import java.util.Map;<br>import lombok.SneakyThrows;<br>import org.json.JSONObject;<br>import org.junit.jupiter.api.BeforeAll;<br>import org.junit.jupiter.api.BeforeEach;<br>import org.junit.jupiter.api.Test;<br>import org.mockserver.client.MockServerClient;<br>import org.mockserver.springtest.MockServerTest;<br>import org.springframework.beans.factory.annotation.Autowired;<br>import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;<br>import org.springframework.context.annotation.Import;<br>import org.springframework.http.HttpHeaders;<br>import org.springframework.test.context.ActiveProfiles;<br>import org.springframework.test.web.servlet.MockMvc;<br><br>@ActiveProfiles(&quot;test&quot;)<br>@MockServerTest<br>@WebMvcTest(controllers = BaseController.class)<br>class BaseControllerTest {<br><br>  private MockServerClient mockServerClient;<br><br>  @Autowired<br>  private MockMvc mockMvc;<br><br>  private static String jwks;<br>  private static String jwt;<br><br>  @BeforeAll<br>  public static void setUp() {<br>    final var keyPair = generateKeyPair();<br>    jwks = generateJwksJson(keyPair);<br>    jwt = authorize(keyPair);<br>  }<br><br>  @BeforeEach<br>  public void setUpServer() {<br>    mockServerClient<br>        .when(<br>            request()<br>                .withMethod(&quot;GET&quot;)<br>                .withPath(&quot;/auth/realms/test-realm/protocol/openid-connect/certs&quot;))<br>        .respond(response().withStatusCode(200).withBody(jwks));<br>  }<br><br>  @SneakyThrows<br>  @Test<br>  void shouldReturnAccountData() {<br>    final var headers = new HttpHeaders();<br>    headers.setBearerAuth(jwt);<br>    mockMvc.perform(get(&quot;/resources/1&quot;).headers(headers))<br>        .andExpect(status().isOk())<br>        .andExpect(jsonPath(&quot;$.name&quot;, is(&quot;Oleksandr Pohorelov&quot;)));<br>  }<br><br>  @SneakyThrows<br>  @Test<br>  void shouldReturnForbidden() {<br>    final var headers = new HttpHeaders();<br>    headers.setBearerAuth(jwt);<br>    mockMvc.perform(get(&quot;/resources/2&quot;).headers(headers))<br>        .andExpect(status().isForbidden())<br>        .andExpect(jsonPath(&quot;$.errorMessage&quot;,<br>            is(&quot;Search account id is not associated with current user&quot;)));<br>  }<br><br>  @SneakyThrows<br>  private static KeyPair generateKeyPair() {<br>    final var keyGenerator = KeyPairGenerator.getInstance(&quot;RSA&quot;);<br>    keyGenerator.initialize(1024);<br>    return keyGenerator.generateKeyPair();<br>  }<br><br>  private static String generateJwksJson(KeyPair keyPair) {<br>    final var rsaKey = new RSAKey.Builder((RSAPublicKey) keyPair.getPublic()).build();<br>    final var jwk =<br>        Map.of(<br>            &quot;kid&quot;, &quot;test&quot;,<br>            &quot;kty&quot;, &quot;RSA&quot;,<br>            &quot;alg&quot;, &quot;RS256&quot;,<br>            &quot;n&quot;, rsaKey.getModulus().toString(),<br>            &quot;e&quot;, rsaKey.getPublicExponent().toString());<br>    final var responseSet = Map.of(&quot;keys&quot;, List.of(jwk));<br>    final var jsonObj = new JSONObject(responseSet);<br>    return jsonObj.toString();<br>  }<br><br>  @SneakyThrows<br>  private static String authorize(KeyPair keyPair) {<br>    return Jwts.builder()<br>        .setHeaderParam(&quot;kid&quot;, &quot;test&quot;)<br>        .signWith(SignatureAlgorithm.RS256, keyPair.getPrivate())<br>        .claim(&quot;resourceId&quot;, &quot;1&quot;)<br>        .claim(&quot;permissions&quot;, &quot;test-permissions&quot;)<br>        .setExpiration(new Date(System.currentTimeMillis() + 5 * 60 * 1000))<br>        .compact();<br>  }<br><br>}</pre><p>To get things work let’s override JWKS URL in application-test.yaml:</p><pre>spring:<br>  security:<br>    oauth2:<br>      resourceserver:<br>        jwt:<br>          jwkSetUri: http://localhost:${mockServerPort}/auth/realms/test-realm/protocol/openid-connect/certs</pre><p>Let’s discuss what we are doing in this test class:</p><ol><li>Before tests execution starts, we create 1 pair of RSA keys.</li><li>Public part of that key we use in JWKS creation. There is a provider’s defined JSON response structure — we follow this structure by putting values to map. Note field kid=test in this map. This is key identifier. Same identifier we are going to put in test JWT token so that Spring will be able to find required key in set.</li><li>We create test JWT with custom claims (required in our business logic). Finally, we sign test JWT with private part of the RSA key.</li><li>Final preparation — before each test we setup mock server expectations. We know that Spring is going to call URL which we have overridden in application test properties. We set created JWKS as an expected response of the mock server.</li></ol><p>That’s it. We have fully operational application context without any mocked beans. We can play with SecurityContext as we want using test JWT.</p><p><em>Final note. I have seen some tests which use real Keycloack instances (in test environment) and call real authorization services (in same environment) to get real JWT token. Needless to say how fragile such tests are, as their execution depends on other development teams. Think twice if you want to have constant headache attempting to run and maintain such tests.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=91c05aa3281a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/testing-spring-boot-java-applications-cookbook/testing-security-without-mocked-beans-91c05aa3281a">Testing Security Without Mocked Beans</a> was originally published in <a href="https://medium.com/testing-spring-boot-java-applications-cookbook">Testing Spring Boot Java Applications Cookbook</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Testing ObjectMapper component]]></title>
            <link>https://medium.com/testing-spring-boot-java-applications-cookbook/testing-objectmapper-component-a3b615bb959f?source=rss----c3c96d817911---4</link>
            <guid isPermaLink="false">https://medium.com/p/a3b615bb959f</guid>
            <category><![CDATA[test]]></category>
            <category><![CDATA[jackson]]></category>
            <category><![CDATA[json]]></category>
            <category><![CDATA[java]]></category>
            <category><![CDATA[spring-boot]]></category>
            <dc:creator><![CDATA[Oleksandr Pohorelov]]></dc:creator>
            <pubDate>Sat, 12 Nov 2022 10:17:14 GMT</pubDate>
            <atom:updated>2022-11-12T10:17:14.197Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*SMy7ZvPJrkDSRX05oqbVdQ.png" /></figure><p>Let’s imagine that we have component, which is mainly responsible for parsing JSON string in model. For example, there is a Kafka listener component, which accept string event, parses it, and redirects to processor service.</p><pre>import com.fasterxml.jackson.databind.ObjectMapper;<br>import com.pohorelov.medium.model.EventModel;<br>import com.pohorelov.medium.service.KafkaProcessor;<br>import lombok.RequiredArgsConstructor;<br>import lombok.SneakyThrows;<br>import org.springframework.kafka.annotation.KafkaListener;<br>import org.springframework.stereotype.Component;<br><br>@Component<br>@RequiredArgsConstructor<br>public class KafkaTypicalListener {<br><br>  private final ObjectMapper objectMapper;<br>  private final KafkaProcessor kafkaProcessor;<br><br>  @SneakyThrows<br>  @KafkaListener(topics = &quot;${spring.kafka.topic.test-topic}&quot;, groupId = &quot;${spring.kafka.groups.test-group}&quot;)<br>  public void processKafkaEvent(String payload) {<br>    final var eventModel = objectMapper.readValue(payload, EventModel.class);<br>    kafkaProcessor.process(eventModel);<br>  }<br><br>}</pre><p>I also prepared simple model structure:</p><pre>import java.time.LocalDateTime;<br>import lombok.AllArgsConstructor;<br>import lombok.Builder;<br>import lombok.NoArgsConstructor;<br>import lombok.Value;<br><br>@Value<br>@Builder<br>@NoArgsConstructor(force = true)<br>@AllArgsConstructor<br>public class EventModel {<br><br>  String name;<br>  LocalDateTime time;<br>  Type type;<br><br>  public enum Type {<br>    LEFT, RIGHT<br>  }<br><br>}</pre><h3>Can we make a simple unit test?</h3><p>We can write simple test — create listener instance with mock objectMapper and processor. Mock object mapper response, verify processor was called with mocked object mapper response. But do we actually achieve anything with this kind of test? We will verify that listener uses ObjectMapper for parsing, and redirects it to processor. But if we have any issues with JSON mapping, are we going to know about it?</p><p>Okay, obviously, we would like to test JSON parsing itself. So instead of mock ObjectMapper — we need a real instance. Probably, we can think about something like this:</p><pre>import static org.assertj.core.api.Assertions.assertThat;<br>import static org.mockito.Mockito.doNothing;<br><br>import com.fasterxml.jackson.databind.ObjectMapper;<br>import com.pohorelov.medium.model.EventModel;<br>import com.pohorelov.medium.service.KafkaProcessor;<br>import java.time.LocalDateTime;<br>import org.junit.jupiter.api.BeforeEach;<br>import org.junit.jupiter.api.Test;<br>import org.junit.jupiter.api.extension.ExtendWith;<br>import org.mockito.ArgumentCaptor;<br>import org.mockito.Captor;<br>import org.mockito.Mock;<br>import org.mockito.junit.jupiter.MockitoExtension;<br><br>@ExtendWith(MockitoExtension.class)<br>class KafkaListenerTest {<br><br>  private KafkaTypicalListener kafkaListener;<br><br>  @Mock<br>  private KafkaProcessor kafkaProcessor;<br><br>  @Captor<br>  private ArgumentCaptor&lt;EventModel&gt; eventModelArgumentCaptor;<br><br>  @BeforeEach<br>  public void setUp() {<br>    kafkaListener = <br>        new KafkaTypicalListener(new ObjectMapper(), kafkaProcessor);<br>  }<br><br>  @Test<br>  void shouldProcessEvent() {<br>    final var payload = &quot;{\&quot;name\&quot; : \&quot;Alex\&quot;, \&quot;time\&quot; : \&quot;2022-11-12T13:01:00\&quot;, \&quot;type\&quot;: \&quot;LEFT\&quot;}&quot;;<br><br>    doNothing().when(kafkaProcessor)<br>        .process(eventModelArgumentCaptor.capture());<br><br>    kafkaListener.processKafkaEvent(payload);<br><br>    final var actualModel = <br>        eventModelArgumentCaptor.getValue();<br>    <br>    assertThat(actualModel.getName()).isEqualTo(&quot;Alex&quot;);<br>    assertThat(actualModel.getTime())<br>        .isEqualTo(LocalDateTime.of(2022, 11, 12, 13, 1, 0));<br>    assertThat(actualModel.getType()).isEqualTo(EventModel.Type.LEFT);<br>  }<br><br>}</pre><p>But such test will fail:</p><pre>com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type `java.time.LocalDateTime` not supported by default: add Module &quot;com.fasterxml.jackson.datatype:jackson-datatype-jsr310&quot; to enable handling<br> at [Source: (String)&quot;{&quot;name&quot; : &quot;Alex&quot;, &quot;time&quot; : &quot;2022-11-12T13:01:00&quot;}&quot;; line: 1, column: 28] (through reference chain: com.pohorelov.medium.model.EventModel[&quot;time&quot;])</pre><p>Conclusion: we would like to test ObjectMapper which we configured for our application (i.e. ObjectMapper from real context) instead of the new instance.</p><h3>Playing with Spring context…</h3><p>Okay, can we ask SpringBoot to create test context of 2 beans, which we want to test?</p><pre>...<br><br>@SpringBootTest(classes = {<br>    KafkaTypicalListener.class,<br>    ObjectMapper.class<br>})<br>class KafkaListenerTest {<br><br>  @Autowired<br>  private KafkaTypicalListener kafkaListener;<br><br>  @MockBean<br>  private KafkaProcessor kafkaProcessor;<br><br>...</pre><p>Same exception… Why? Because plain ObjectMapper cannot deserialize LocalDateTime by default. We don’t face this issue is runtime, because Spring auto-configure default module and features of ObjectMapper for us. This is happening behind the scene, but we do need this part in out test as well.</p><h3>Use bootstrap annotation</h3><p>As we discussed <a href="https://medium.com/testing-spring-boot-java-applications-cookbook/testing-client-component-c207cf13bad6">in a previous story</a>, there are some test annotations provided by Spring Boot Test, which can help to setup particular “slice” of application, which we want to test. In our case, all we need to do is to utilize @AutoConfigureJson annotation, which will include several JSON related auto-configurations to our test application context:</p><pre>@SpringBootTest(classes = KafkaTypicalListener.class)<br>@AutoConfigureJson<br>class KafkaListenerTest {<br>...</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cYMfaUPMqDm_7poQDl7rlg.png" /></figure><h3>Don’t forget to include customizers</h3><p>If you have ObjectMapper customizer bean in your configuration:</p><pre>import com.fasterxml.jackson.annotation.JsonInclude;<br>import com.fasterxml.jackson.databind.DeserializationFeature;<br>import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;<br>import org.springframework.context.annotation.Bean;<br>import org.springframework.context.annotation.Configuration;<br><br>@Configuration<br>public class JacksonConfiguration {<br><br>  @Bean<br>  public Jackson2ObjectMapperBuilderCustomizer customJackson2ObjectMapperBuilder() {<br>    return builder -&gt; {<br>      builder.serializationInclusion(JsonInclude.Include.NON_NULL);<br>      builder.featuresToEnable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL);<br>    };<br>  }<br><br>}</pre><p>Don’t forget to include it to your test context as well:</p><pre>import static org.assertj.core.api.Assertions.assertThat;<br>import static org.mockito.Mockito.doNothing;<br><br>import com.pohorelov.medium.configuration.JacksonConfiguration;<br>import com.pohorelov.medium.model.EventModel;<br>import com.pohorelov.medium.service.KafkaProcessor;<br>import java.time.LocalDateTime;<br>import org.junit.jupiter.api.Test;<br>import org.mockito.ArgumentCaptor;<br>import org.mockito.Captor;<br>import org.springframework.beans.factory.annotation.Autowired;<br>import org.springframework.boot.test.autoconfigure.json.AutoConfigureJson;<br>import org.springframework.boot.test.context.SpringBootTest;<br>import org.springframework.boot.test.mock.mockito.MockBean;<br><br>@SpringBootTest(classes = {<br>    KafkaTypicalListener.class,<br>    JacksonConfiguration.class<br>})<br>@AutoConfigureJson<br>class KafkaListenerTest {<br><br>  @Autowired<br>  private KafkaTypicalListener kafkaListener;<br><br>  @MockBean<br>  private KafkaProcessor kafkaProcessor;<br><br>  @Captor<br>  private ArgumentCaptor&lt;EventModel&gt; eventModelArgumentCaptor;<br><br>  @Test<br>  void shouldProcessEvent() {<br>    final var payload = &quot;{\&quot;name\&quot; : \&quot;Alex\&quot;, \&quot;time\&quot; : \&quot;2022-11-12T13:01:00\&quot;, \&quot;type\&quot;: \&quot;INVALID\&quot;}&quot;;<br><br>    doNothing().when(kafkaProcessor)<br>        .process(eventModelArgumentCaptor.capture());<br><br>    kafkaListener.processKafkaEvent(payload);<br><br>    final var actualModel =<br>        eventModelArgumentCaptor.getValue();<br><br>    assertThat(actualModel.getName()).isEqualTo(&quot;Alex&quot;);<br>    assertThat(actualModel.getTime())<br>        .isEqualTo(LocalDateTime.of(2022, 11, 12, 13, 1, 0));<br>    assertThat(actualModel.getType()).isNull();<br>  }<br><br>}</pre><h3>Summary</h3><ul><li>Unit test is the simplest kind of tests developer can prepare. In most cases (especially, for services, where we have business logic), unit tests should be the only choice. However, some layers of application may require “component”, or “slice” testing. Such tests should include target component as well as some part of configured application context.</li><li>Spring Boot provides us with useful tool for testing of JSON manipulations (ie. serialization and desialization). Consider utilizing @AutoConfigureJson annotation while testing Kafka listeners (with String consumer deserializer) or JSON schema validators.</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a3b615bb959f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/testing-spring-boot-java-applications-cookbook/testing-objectmapper-component-a3b615bb959f">Testing ObjectMapper component</a> was originally published in <a href="https://medium.com/testing-spring-boot-java-applications-cookbook">Testing Spring Boot Java Applications Cookbook</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Testing Client Component]]></title>
            <link>https://medium.com/testing-spring-boot-java-applications-cookbook/testing-client-component-c207cf13bad6?source=rss----c3c96d817911---4</link>
            <guid isPermaLink="false">https://medium.com/p/c207cf13bad6</guid>
            <category><![CDATA[java]]></category>
            <category><![CDATA[webclient]]></category>
            <category><![CDATA[spring]]></category>
            <category><![CDATA[resttemplate]]></category>
            <category><![CDATA[testing]]></category>
            <dc:creator><![CDATA[Oleksandr Pohorelov]]></dc:creator>
            <pubDate>Wed, 09 Nov 2022 09:12:31 GMT</pubDate>
            <atom:updated>2022-11-09T09:12:31.149Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YsUu96aZQXFaejlU-QWU3w.png" /></figure><h3>What is client component?</h3><p>Client Component is a class which responsibility is to call other services or 3rd parties. It has an injected RestTemplate (from spring-boot-starter-web) or WebClient (from spring-boot-starter-webflux). Third party URL is usually configured in property files and is injected in the component with Spring Value annotation or via ConfigurationProperties class.</p><p>application.yaml</p><pre>application:<br>  third-party:<br>    base-url: https://api.coinbase.com<br>    currency-endpoint: ${application.third-party.base-url}/v2/currencies</pre><p>ApplicationProperties.java</p><pre>import lombok.Getter;<br>import lombok.Setter;<br>import org.springframework.boot.context.properties.ConfigurationProperties;<br>import org.springframework.context.annotation.Configuration;<br><br>@Getter<br>@Setter<br>@Configuration<br>@ConfigurationProperties(prefix = &quot;application.third-party&quot;)<br>public class ApplicationProperties {<br><br>  private String currencyEndpoint;<br><br>}</pre><blockquote>Fetching properties via configuration class should be considered as a preferable way. Imagine situation when service needs to integrate with some amount of endpoints. Injecting each endpoint with Value can expand client’s constructor and decrease readability. Instead we can keep one configuration properties class and get any required endpoint with getter.</blockquote><p>TypicalClient.java</p><pre>import com.pohorelov.medium.configuration.ApplicationProperties;<br>import com.pohorelov.medium.response.CurrencyResponse;<br>import lombok.RequiredArgsConstructor;<br>import org.springframework.stereotype.Component;<br>import org.springframework.web.reactive.function.client.WebClient;<br><br>@Component<br>@RequiredArgsConstructor<br>public class TypicalClient {<br><br>  private final WebClient webClient;<br>  private final ApplicationProperties applicationProperties;<br><br>  public CurrencyResponse getCurrencies() {<br>    return webClient<br>        .get()<br>        .uri(applicationProperties.getCurrencyEndpoint())<br>        .retrieve()<br>        .bodyToMono(CurrencyResponse.class)<br>        .block();<br>  }<br><br>}</pre><blockquote>WebClient should be considered as a preferable tool for a client component. RestTemplate is in maintenance mode as mentioned in its <a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html">documentation</a>.</blockquote><p>Same client with RestTemplate will look like:</p><pre>import com.pohorelov.medium.configuration.ApplicationProperties;<br>import com.pohorelov.medium.response.CurrencyResponse;<br>import lombok.RequiredArgsConstructor;<br>import org.springframework.stereotype.Component;<br>import org.springframework.web.client.RestTemplate;<br><br>@Component<br>@RequiredArgsConstructor<br>public class TypicalRestTemplateClient {<br><br>  private final RestTemplate restTemplate;<br>  private final ApplicationProperties applicationProperties;<br><br>  public CurrencyResponse getCurrencies() {<br>    return restTemplate.getForObject(<br>        applicationProperties.getCurrencyEndpoint(), <br>        CurrencyResponse.class<br>    );<br>  }<br><br>}</pre><h3>What exactly we want to test?</h3><p>To choose the best testing tool and decide on testing strategy, we should always first answer this question. In most cases, good old unit test would be the best option, but can it address all our requirements?</p><p>In case of client components we would like to test the following:</p><ul><li>url is configured correctly in properties or/and in @ConfigurationProperties class. In our case it means that we expect client to use application.third-party.currency-endpoint. Moreover, we would like to know that developers use base url to constuct endpoint. So that if we replace it for our test — our client should use this value.</li><li>client is sending request to the configured url in a tested method. This point also implies configured request params and headers. There can be a situation when request is “enriched” by Spring Filters (e.g., we have microservice architecture and use distributed tracing, so our common web filter adds a correlation id header to the request).</li><li>request body format (if present)</li><li>verify response body is deserialised correctly</li></ul><h3>What about plain unit test?</h3><p>We still can try to meet all the points with plain unit test. We can mock ApplicationProperties and verify that expected getter was called.</p><p>Mocking WebClient can be a little bit tricky. There are StackOverFlow discussions dedicated to this. <a href="https://stackoverflow.com/questions/45301220/how-to-mock-spring-webflux-webclient">One of them</a> recommends refactoring client code with ExchangeFunction or mocking each step of WebClient call (ie. mock .get() then mock .uri(), then mock .retrieve() and so on).</p><p>Mocking RestTemplate would be much easier — when template was called for specific URI and for specific response class — then return prepared answer.</p><p>But let’s take a look at last point of our requirements. In our api response structure is the following:</p><pre>{<br>  &quot;data&quot;: [<br>    {<br>      &quot;id&quot;: &quot;AED&quot;,<br>      &quot;name&quot;: &quot;United Arab Emirates Dirham&quot;,<br>      &quot;min_size&quot;: &quot;0.01000000&quot;<br>    },<br>    {<br>      &quot;id&quot;: &quot;AFN&quot;,<br>      &quot;name&quot;: &quot;Afghan Afghani&quot;,<br>      &quot;min_size&quot;: &quot;0.01000000&quot;<br>    }<br>  ]<br>}</pre><p>I prepared response object to represent it:</p><pre>import java.util.List;<br>import lombok.AllArgsConstructor;<br>import lombok.Builder;<br>import lombok.NoArgsConstructor;<br>import lombok.Value;<br><br>@Value<br>@Builder<br>@NoArgsConstructor(force = true)<br>@AllArgsConstructor<br>public class CurrencyResponse {<br><br>  List&lt;CurrencyData&gt; data;<br><br>}</pre><pre>import com.fasterxml.jackson.annotation.JsonProperty;<br>import lombok.AllArgsConstructor;<br>import lombok.Builder;<br>import lombok.NoArgsConstructor;<br>import lombok.Value;<br><br>@Value<br>@Builder<br>@NoArgsConstructor(force = true)<br>@AllArgsConstructor<br>public class CurrencyData {<br><br>  String id;<br>  String name;<br>  @JsonProperty(&quot;min_size&quot;)<br>  String minSize;<br><br>}</pre><p>Note field minSize. I don’t want to violate Java Code Convention — so I named field in camel case. However, in JSON its name is in snake case. Returning manually built response object I will not be able to test mapping of this field.</p><h3>Proposed solution — RestClientTest</h3><p>Hopefully, Spring developers prepared a set of test annotation which can help to bootstrap application context in a specific way to test a particular layer (or slice) of our application.</p><p>You can check which auto-configurations are invoked for which test annotation here:</p><p><a href="https://docs.spring.io/spring-boot/docs/current/reference/html/test-auto-configuration.html">Test Auto-configuration Annotations</a></p><p>Let’s write test for RestTemplate client:</p><pre>import static org.assertj.core.api.Assertions.<em>assertThat</em>;<br>import static org.springframework.test.web.client.match.MockRestRequestMatchers.<em>requestTo</em>;<br>import static org.springframework.test.web.client.response.MockRestResponseCreators.<em>withSuccess</em>;<br><br>import com.pohorelov.medium.configuration.ApplicationProperties;<br>import java.nio.charset.Charset;<br>import lombok.SneakyThrows;<br>import org.junit.jupiter.api.Test;<br>import org.springframework.beans.factory.annotation.Autowired;<br>import org.springframework.boot.context.properties.EnableConfigurationProperties;<br>import org.springframework.boot.test.autoconfigure.web.client.AutoConfigureWebClient;<br>import org.springframework.boot.test.autoconfigure.web.client.RestClientTest;<br>import org.springframework.core.io.ClassPathResource;<br>import org.springframework.http.MediaType;<br>import org.springframework.test.context.ActiveProfiles;<br>import org.springframework.test.web.client.MockRestServiceServer;<br>import org.springframework.util.StreamUtils;<br><br>@ActiveProfiles(&quot;test&quot;)<br>@RestClientTest(components = TypicalRestTemplateClient.class)<br>@AutoConfigureWebClient(registerRestTemplate = true)<br>@EnableConfigurationProperties(ApplicationProperties.class)<br>class TypicalRestTemplateClientTest {<br><br>  @Autowired<br>  private TypicalRestTemplateClient client;<br><br>  @Autowired<br>  private MockRestServiceServer server;<br><br>  @SneakyThrows<br>  @Test<br>  void shouldReturnData() {<br>    final var responseStream =<br>        new ClassPathResource(&quot;client/response.json&quot;)<br>            .getInputStream();<br>    final var response =<br>        StreamUtils.<em>copyToString</em>(responseStream, <br>            Charset.<em>defaultCharset</em>());<br>    this.server.expect(<br>        <em>requestTo</em>(&quot;http://localhost:8080/v2/currencies&quot;)<br>        )<br>        .andRespond(<br>            <em>withSuccess</em>(response, MediaType.<em>APPLICATION_JSON</em>)<br>        );<br><br>    final var actualResponse = <br>        client.getCurrencies();<br><br>    <em>assertThat</em>(actualResponse).isNotNull();<br>    <em>assertThat</em>(actualResponse.getData()).hasSize(2);<br>    <em>assertThat</em>(actualResponse.getData().get(0).getId())<br>        .isEqualTo(&quot;AED&quot;);<br>    <em>assertThat</em>(actualResponse.getData().get(0).getName())<br>        .isEqualTo(&quot;United Arab Emirates Dirham&quot;);<br>    <em>assertThat</em>(actualResponse.getData().get(0).getMinSize())<br>        .isEqualTo(&quot;0.01000000&quot;);<br>  }<br><br>}</pre><p>We use active profile to get base url from application-test.yaml:</p><pre>application:<br>  third-party:<br>    base-url: http://localhost:8080</pre><blockquote>Note that MockRestServiceServer was auto-configured for us and was bound to RestTemplate present in application context.</blockquote><p>Test structure is the following:</p><ol><li>Register mock server expectations (and set response JSON string).</li><li>Call target method.</li><li>Assert deserialized response meets our expectations.</li></ol><h3>Testing component with WebClient</h3><p>RestClient test cannot be used for client implementation with WebClient, because MockRestServiceServer will be not be able to find required RestTemplate for binding. Spring developers still recommend to use static server for testing in their <a href="https://docs.spring.io/spring-framework/docs/5.2.6.RELEASE/spring-framework-reference/web-reactive.html#webflux-client-testing">documentation</a>.</p><p>In order to achieve this we are going to add mock-server dependency:</p><pre>&lt;dependency&gt;<br>    &lt;groupId&gt;org.mock-server&lt;/groupId&gt;<br>    &lt;artifactId&gt;mockserver-spring-test-listener-no-dependencies&lt;/artifactId&gt;<br>    &lt;version&gt;5.14.0&lt;/version&gt;<br>&lt;/dependency&gt;</pre><p>Mock server will be started on random free port, so we should also update application-test.yaml property:</p><pre>application:<br>  third-party:<br>    base-url: <a href="http://localhost:${mockServerPort}">http://localhost:${mockServerPort}</a></pre><p>Note that you should not specifymockServerPort, it will be defined after mock server is started.</p><p>Test class will look like the following:</p><pre>import static java.nio.charset.Charset.<em>defaultCharset</em>;<br>import static org.assertj.core.api.Assertions.<em>assertThat</em>;<br>import static org.mockserver.model.HttpRequest.<em>request</em>;<br>import static org.mockserver.model.HttpResponse.<em>response</em>;<br><br>import com.pohorelov.medium.configuration.ApplicationProperties;<br>import com.pohorelov.medium.configuration.WebClientConfiguration;<br>import lombok.SneakyThrows;<br>import org.junit.jupiter.api.Test;<br>import org.mockserver.client.MockServerClient;<br>import org.mockserver.model.MediaType;<br>import org.mockserver.springtest.MockServerTest;<br>import org.springframework.beans.factory.annotation.Autowired;<br>import org.springframework.boot.context.properties.EnableConfigurationProperties;<br>import org.springframework.boot.test.context.SpringBootTest;<br>import org.springframework.core.io.ClassPathResource;<br>import org.springframework.test.context.ActiveProfiles;<br>import org.springframework.util.StreamUtils;<br><br>@ActiveProfiles(&quot;test&quot;)<br>@SpringBootTest(classes = {<br>    TypicalClient.class,<br>    WebClientConfiguration.class<br>})<br>@MockServerTest<br>@EnableConfigurationProperties(ApplicationProperties.class)<br>public class TypicalClientTest {<br><br>  private MockServerClient mockServerClient;<br><br>  @Autowired<br>  private TypicalClient client;<br><br>  @SneakyThrows<br>  @Test<br>  void shouldReturnData() {<br>    final var responseStream =<br>        new ClassPathResource(&quot;client/response.json&quot;)<br>            .getInputStream();<br>    final var response =<br>        StreamUtils.<em>copyToString</em>(responseStream, <em>defaultCharset</em>());<br><br>    this.mockServerClient.when(<br>        <em>request</em>()<br>            .withMethod(&quot;GET&quot;)<br>            .withPath(&quot;/v2/currencies&quot;))<br>        .respond(<em>response</em>()<br>            .withContentType(MediaType.<em>APPLICATION_JSON</em>)<br>            .withBody(response));<br><br>    final var actualResponse = client.getCurrencies();<br><br>    <em>assertThat</em>(actualResponse).isNotNull();<br>    <em>assertThat</em>(actualResponse.getData()).hasSize(2);<br>    <em>assertThat</em>(actualResponse.getData().get(0).getId())<br>        .isEqualTo(&quot;AED&quot;);<br>    <em>assertThat</em>(actualResponse.getData().get(0).getName())<br>        .isEqualTo(&quot;United Arab Emirates Dirham&quot;);<br>    <em>assertThat</em>(actualResponse.getData().get(0).getMinSize())<br>        .isEqualTo(&quot;0.01000000&quot;);<br>  }<br><br>}</pre><p>We changed expectation format settings, but approach and assertions remained the same.</p><p>Note that for MacOS (M1 chip) you might also need the following dependency:</p><pre>&lt;dependency&gt;<br>    &lt;groupId&gt;io.netty&lt;/groupId&gt;<br>    &lt;artifactId&gt;netty-resolver-dns-native-macos&lt;/artifactId&gt;<br>    &lt;version&gt;4.1.84.Final&lt;/version&gt;<br>    &lt;classifier&gt;osx-aarch_64&lt;/classifier&gt;<br>&lt;/dependency&gt;</pre><h3>Summary</h3><p>Client Components are responsible for REST communication with 3rd parties and other micro-services. While testing such components, one should validate request configuration and serialization as well as server response deserialization (and probably, exception handling).</p><p>Unit tests may be not the best solution as one would not be able to verify some aspects of request/response structure. Loading full application context is never a good choice while testing particular application layer.</p><p>Better approach would be to bootstrap application context with client component as well as components required for REST communication (e.g., WebClient configuration, configuration properties). Spring Boot Test annotation @RestClientTest can help to setup context for clients which use RestTemplate. For WebClient one should still setup mock server manually. However, @MockServerTest from mockserver-spring-test-listener can help a little bit with it (it takes responsibility for server start and shutdown logic).</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c207cf13bad6" width="1" height="1" alt=""><hr><p><a href="https://medium.com/testing-spring-boot-java-applications-cookbook/testing-client-component-c207cf13bad6">Testing Client Component</a> was originally published in <a href="https://medium.com/testing-spring-boot-java-applications-cookbook">Testing Spring Boot Java Applications Cookbook</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>