{"id":835,"date":"2020-03-30T06:00:00","date_gmt":"2020-03-30T06:00:00","guid":{"rendered":"http:\/\/bullyrooks.com\/index.php\/2020\/03\/30\/simple-spring-boot-service-to-kubernetes-application-step-6-c52f5247ba20\/"},"modified":"2021-02-04T01:49:09","modified_gmt":"2021-02-04T01:49:09","slug":"simple-spring-boot-service-to-kubernetes-application-step-6-c52f5247ba20","status":"publish","type":"post","link":"https:\/\/bullyrooks.com\/index.php\/2020\/03\/30\/simple-spring-boot-service-to-kubernetes-application-step-6-c52f5247ba20\/","title":{"rendered":"Documentation and Code Coverage"},"content":{"rendered":"\n<p class=\"graf graf--p graf-after--h3 graf--trailing\" id=\"2a34\">Before we move on to getting our service ready for deployment we need to do a little more housecleaning. Sometimes these tasks get de-prioritized so we might as well take care of it now, before we move on.<\/p>\n\n\n\n<h3 class=\"graf graf--h3 graf--leading wp-block-heading\" id=\"c540\">Endpoint Documentation<\/h3>\n\n\n\n<p class=\"graf graf--p graf-after--h3\" id=\"a431\">Using OpenAPI is a powerful tool for documenting our REST endpoints. Documenting our endpoints reduces the amount of unplanned time we need to take from our priorities to support our users or clients of our service. Additionally, it can also be used to <strong class=\"markup--strong markup--p-strong\">generate the clients<\/strong>, so that we don\u2019t have to build and maintain client libraries.<\/p>\n\n\n\n<p class=\"graf graf--p graf-after--p\" id=\"9110\">First we need to add the dependencies to our maven pom file.<\/p>\n\n\n\n<pre id=\"0ccc\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>&lt;properties&gt;\n   ...\n   &lt;springdoc-openapi-core.version&gt;1.1.49&lt;\/springdoc-openapi-core.version&gt;\n&lt;\/properties&gt;<\/code><\/pre>\n\n\n\n<pre id=\"8832\" class=\"wp-block-code graf graf--pre graf-after--pre\"><code>&lt;dependencies&gt;\n...\n   &lt;!-- documentation --&gt;\n   &lt;dependency&gt;\n      &lt;groupId&gt;org.springdoc&lt;\/groupId&gt;\n      &lt;artifactId&gt;springdoc-openapi-ui&lt;\/artifactId&gt;\n      &lt;version&gt;${springdoc-openapi-core.version}&lt;\/version&gt;\n   &lt;\/dependency&gt;\n...\n&lt;\/dependencies&gt;<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre graf--trailing\" id=\"ecd7\">Restart our application and go to <code class=\"markup--code markup--p-code\"><a class=\"markup--anchor markup--p-anchor\" href=\"http:\/\/localhost:10000\/swagger-ui\/\" target=\"_blank\" rel=\"noopener\" data-href=\"http:\/\/localhost:10000\/swagger-ui\/\">http:\/\/localhost:10000\/swagger-ui\/<\/a><\/code>. You should see the swagger ui and you can see our endpoint. You can also confirm on the <code class=\"markup--code markup--p-code\">CustomerDTO <\/code>schema that the validation requirements are documented.<\/p>\n\n\n\n<h3 class=\"graf graf--h3 graf--leading wp-block-heading\" id=\"b597\">Code Coverage<\/h3>\n\n\n\n<p class=\"graf graf--p graf-after--h3\" id=\"6ac5\">We really need to be aware of how our code is evolving. One particularly useful metric is code coverage. We\u2019ll be following <a class=\"markup--anchor markup--p-anchor\" href=\"https:\/\/www.baeldung.com\/jacoco\" target=\"_blank\" rel=\"noopener\" data-href=\"https:\/\/www.baeldung.com\/jacoco\">this example<\/a> to configure our project to generate code coverage reports via jacoco.<\/p>\n\n\n\n<p class=\"graf graf--p graf-after--p\" id=\"1d48\">Add this configuration to the <code class=\"markup--code markup--p-code\">pom.xml<\/code> file:<\/p>\n\n\n\n<pre id=\"c0a8\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>&lt;properties&gt;\n   ...\n   &lt;jacoco-maven-plugin.version&gt;0.8.5&lt;\/jacoco-maven-plugin.version&gt;\n&lt;\/properties&gt;<\/code><\/pre>\n\n\n\n<pre id=\"5a2b\" class=\"wp-block-code graf graf--pre graf-after--pre\"><code>&lt;build&gt;\n   &lt;plugins&gt;\n      ...\n      &lt;plugin&gt;\n         &lt;groupId&gt;org.jacoco&lt;\/groupId&gt;\n         &lt;artifactId&gt;jacoco-maven-plugin&lt;\/artifactId&gt;\n         &lt;version&gt;${jacoco-maven-plugin.version}&lt;\/version&gt;\n         &lt;executions&gt;\n            &lt;execution&gt;\n               &lt;goals&gt;\n                  &lt;goal&gt;prepare-agent&lt;\/goal&gt;\n               &lt;\/goals&gt;\n            &lt;\/execution&gt;\n            &lt;execution&gt;\n               &lt;id&gt;report&lt;\/id&gt;\n               &lt;phase&gt;prepare-package&lt;\/phase&gt;\n               &lt;goals&gt;\n                  &lt;goal&gt;report&lt;\/goal&gt;\n               &lt;\/goals&gt;\n            &lt;\/execution&gt;\n         &lt;\/executions&gt;\n      &lt;\/plugin&gt;\n   &lt;\/plugins&gt;\n\n&lt;\/build&gt;<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"c3f1\">And build the report with <code class=\"markup--code markup--p-code\">mvn clean test jacoco:report<\/code>. View the report at <code class=\"markup--code markup--p-code\">\/target\/site\/jacoco\/index.html<\/code>. Its should look something like this:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2021\/02\/1L1cIJc3E3Jy0KAi5xI81nQ.png?w=960\" alt=\"\" data-recalc-dims=\"1\"\/><\/figure>\n\n\n\n<p class=\"graf graf--p graf-after--figure\" id=\"7119\">Which looks kind of bad, but its actually failing because of all of the generated Lombok code (as well as some classes we shouldn\u2019t be testing). Lets exclude that from the report. Finally, we see that the controller advice as well as the <code class=\"markup--code markup--p-code\">config <\/code>package are being analyzed. Lets move controller advice to <code class=\"markup--code markup--p-code\">config <\/code>and exclude the <code class=\"markup--code markup--p-code\">config <\/code>directory. Additionally, lets exclude the spring boot Application class and the exceptions from the<code class=\"markup--code markup--p-code\"> jacoco-maven-plugin<\/code> maven plugin definition.<\/p>\n\n\n\n<pre id=\"5939\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>&lt;\/execution&gt;\n   &lt;\/executions&gt;\n   &lt;configuration&gt;\n      &lt;excludes&gt;\n         &lt;exclude&gt;com\/brianrook\/medium\/customer\/config\/**\/*&lt;\/exclude&gt;\n         &lt;exclude&gt;com\/brianrook\/medium\/customer\/*Application*&lt;\/exclude&gt;\n         &lt;exclude&gt;com\/brianrook\/medium\/customer\/exception\/**\/*&lt;\/exclude&gt;\n      &lt;\/excludes&gt;\n   &lt;\/configuration&gt;\n&lt;\/plugin&gt;<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"25d2\">Rerunning our report looks much better, but we can see two issues. One is that the code generated by mapstruct has some untested paths. We can and should write tests for these because mapping classes are high risk. If you look at the report closely, you can see that we don\u2019t have test cases around null input. These tests are easily written.<\/p>\n\n\n\n<pre id=\"64e5\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>package com.brianrook.medium.customer.controller.mapper;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.<em class=\"markup--em markup--pre-em\">assertThat<\/em>;\n\npublic class CustomerDTOMapperTest {\n    @Test\n    public void testNullCustomer(){\n<em class=\"markup--em markup--pre-em\">assertThat<\/em>(CustomerDTOMapper.<em class=\"markup--em markup--pre-em\">INSTANCE<\/em>.customerToCustomerDTO(null)).isNull();\n    }\n    @Test\n    public void testNullCustomerDTO(){\n<em class=\"markup--em markup--pre-em\">assertThat<\/em>(CustomerDTOMapper.<em class=\"markup--em markup--pre-em\">INSTANCE<\/em>.customerDTOToCustomer(null)).isNull();\n    }\n}<\/code><\/pre>\n\n\n\n<pre id=\"3dee\" class=\"wp-block-code graf graf--pre graf-after--pre\"><code>package com.brianrook.medium.customer.dao.mapper;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.<em class=\"markup--em markup--pre-em\">assertThat<\/em>;\n\npublic class CustomerEntityMapperTest {\n    @Test\n    public void testNullCustomer(){\n<em class=\"markup--em markup--pre-em\">assertThat<\/em>(CustomerEntityMapper.<em class=\"markup--em markup--pre-em\">INSTANCE<\/em>.customerToCustomerEntity(null)).isNull();\n    }\n    @Test\n    public void testNullCustomerDTO(){\n<em class=\"markup--em markup--pre-em\">assertThat<\/em>(CustomerEntityMapper.<em class=\"markup--em markup--pre-em\">INSTANCE<\/em>.customerEntityToCustomer(null)).isNull();\n    }\n}<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"8010\">More importantly, we can see that the service class has an untested exception path. We could easily write a unit test here that mocks the DAO and throws an exception. I would say: please don\u2019t do that! This is a path that we added for a specific behavior, so lets create a behavior test. Add this additional test to <code class=\"markup--code markup--p-code\">CustomerControllerTest<\/code>:<\/p>\n\n\n\n<pre id=\"6826\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>@Test\npublic void testAddCustomerConflict() throws URISyntaxException\n{\n    CustomerEntity savedRecord = CustomerEntity.<em class=\"markup--em markup--pre-em\">builder<\/em>()\n            .firstName(\"test\")\n            .lastName(\"user\")\n            .email(\"test.user@test.com\")\n            .phoneNumber(\"(123)654-7890\")\n            .build();\n    customerDAO.save(savedRecord);\n\n    RestTemplate restTemplate = new RestTemplate();\n    String baseUrl = \"http:\/\/localhost:\"+randomServerPort+customerPath;\n    URI uri = new URI(baseUrl);\n    CustomerDTO customerDTO = CustomerDTO.<em class=\"markup--em markup--pre-em\">builder<\/em>()\n            .firstName(savedRecord.getFirstName())\n            .lastName(savedRecord.getLastName())\n            .email(savedRecord.getEmail())\n            .phoneNumber(savedRecord.getPhoneNumber())\n            .build();\n\n    HttpHeaders headers = new HttpHeaders();\n    headers.set(HttpHeaders.<em class=\"markup--em markup--pre-em\">CONTENT_TYPE<\/em>, MediaType.<em class=\"markup--em markup--pre-em\">APPLICATION_JSON_VALUE<\/em>);\n\n    HttpEntity&lt;CustomerDTO&gt; request = new HttpEntity&lt;&gt;(customerDTO, headers);\n\n    try {\n        ResponseEntity&lt;CustomerDTO&gt; result = restTemplate.postForEntity(uri, request, CustomerDTO.class);\n\n    }catch (HttpServerErrorException e) {\n        \/\/Verify request failed\n        Assertions.<em class=\"markup--em markup--pre-em\">assertEquals<\/em>(500, e.getRawStatusCode());\n    }\n}<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"6de2\">Rerun the report. We should have 100% coverage now. The best part is it only took 3 test classes and 6 tests to get there.<\/p>\n\n\n\n<h3 class=\"graf graf--h3 graf-after--p wp-block-heading\" id=\"25ad\">Build and&nbsp;Commit<\/h3>\n\n\n\n<pre id=\"c153\" class=\"wp-block-code graf graf--pre graf-after--h3\"><code>git checkout -b coverage\nmvn clean install\ngit add .\ngit commit -m \"OpenAPI docs and code coverage\"\ngit push --set-upstream origin coverage\ngit checkout master\ngit merge coverage\ngit push<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<div class=\"entry-summary\">\nBefore we move on to getting our service ready for deployment we&hellip;\n<\/div>\n<div class=\"link-more\"><a href=\"https:\/\/bullyrooks.com\/index.php\/2020\/03\/30\/simple-spring-boot-service-to-kubernetes-application-step-6-c52f5247ba20\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &ldquo;Documentation and Code Coverage&rdquo;<\/span>&hellip;<\/a><\/div>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[41],"tags":[70,71,68,42,43,69],"course":[40],"class_list":["post-835","post","type-post","status-publish","format-standard","hentry","category-software-development","tag-code-coverage","tag-jacoco","tag-openapi","tag-spring","tag-spring-boot","tag-swagger","course-spring-with-kubernetes","entry"],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":1447,"url":"https:\/\/bullyrooks.com\/index.php\/2023\/05\/14\/unit-tests-vs-component-tests-which-ones-better-for-software-quality\/","url_meta":{"origin":835,"position":0},"title":"Unit Tests vs. Component Tests: Which One&#8217;s Better for Software Quality?","author":"Bullyrook","date":"May 14, 2023","format":false,"excerpt":"Dive into the good and bad of unit tests and component tests in software development. Find out how to get the best of both worlds for top-notch code quality.","rel":"","context":"In &quot;Software Development&quot;","block_context":{"text":"Software Development","link":"https:\/\/bullyrooks.com\/index.php\/category\/software-development\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1264,"url":"https:\/\/bullyrooks.com\/index.php\/2022\/02\/13\/kube-cloud-pt3-health-indicators\/","url_meta":{"origin":835,"position":1},"title":"Kube Cloud Pt3 | Health Indicators","author":"Bullyrook","date":"February 13, 2022","format":false,"excerpt":"Spring offers a way to tell if your services and their dependent resources are up and healthy. Kubernetes can leverage this functionality via their liveness and readiness probes to report if pods are available to service requests. In this session, we're going to enable and connect those health checks. Enable\u2026","rel":"","context":"In &quot;Software Development&quot;","block_context":{"text":"Software Development","link":"https:\/\/bullyrooks.com\/index.php\/category\/software-development\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-4.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-4.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-4.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":1133,"url":"https:\/\/bullyrooks.com\/index.php\/2021\/07\/25\/confirming-the-continuous-deployment-pipeline\/","url_meta":{"origin":835,"position":2},"title":"Confirming the Continuous Deployment Pipeline","author":"Bullyrook","date":"July 25, 2021","format":false,"excerpt":"Lets verify that changes to our application will be automatically available when they're pushed up. Update the Application Let's start a new branch $ git checkout -b \"greeting-change\" Now update our service to return a different greeting: @Component public class HelloWorldService { public HelloWorld helloWorld(String name) { return HelloWorld.builder() .response(\"G'Day,\u2026","rel":"","context":"In &quot;Software Development&quot;","block_context":{"text":"Software Development","link":"https:\/\/bullyrooks.com\/index.php\/category\/software-development\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2021\/07\/image-48.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2021\/07\/image-48.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2021\/07\/image-48.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2021\/07\/image-48.png?resize=1050%2C600&ssl=1 3x"},"classes":[]},{"id":820,"url":"https:\/\/bullyrooks.com\/index.php\/2020\/03\/30\/simple-spring-boot-service-to-kubernetes-application-step-17-3867eb38d8f1\/","url_meta":{"origin":835,"position":3},"title":"Containerizing our UI","author":"Bullyrook","date":"March 30, 2020","format":false,"excerpt":"We\u2019ve got a simple page that calls one of our endpoints working. Let\u2019s go ahead an containerize it so that we can deploy it into our k8s environment in future steps. Build the Dockerfile I\u2019m following the process laid out here, with some minor differences. Create a Dockerfile # Stage\u2026","rel":"","context":"In &quot;Software Development&quot;","block_context":{"text":"Software Development","link":"https:\/\/bullyrooks.com\/index.php\/category\/software-development\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":828,"url":"https:\/\/bullyrooks.com\/index.php\/2020\/03\/30\/simple-spring-boot-service-to-kubernetes-application-step-4-dba10da3d834\/","url_meta":{"origin":835,"position":4},"title":"Create a REST Controller","author":"Bullyrook","date":"March 30, 2020","format":false,"excerpt":"In this article we\u2019re going to take the service layer we created in the last step and add a REST interface to it. We\u2019re going to follow the hexagonal design principles we previously discussed and add a functional or component test and explain how those test work and why they\u2019re\u2026","rel":"","context":"In &quot;Software Development&quot;","block_context":{"text":"Software Development","link":"https:\/\/bullyrooks.com\/index.php\/category\/software-development\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":819,"url":"https:\/\/bullyrooks.com\/index.php\/2020\/03\/30\/simple-spring-boot-service-to-kubernetes-application-step-3-f03bdfe30ceb\/","url_meta":{"origin":835,"position":5},"title":"Building a Service Layer","author":"Bullyrook","date":"March 30, 2020","format":false,"excerpt":"In this stage we\u2019re going to build out the service or logical layer of our microservice. I\u2019ll explain adapter and port or hexagonal architecture and covering the usage of mapstruct. Service Design Ports and adapter or hexagonal design is a well discussed concept, so I\u2019m not going to go into\u2026","rel":"","context":"In &quot;Software Development&quot;","block_context":{"text":"Software Development","link":"https:\/\/bullyrooks.com\/index.php\/category\/software-development\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/posts\/835","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/comments?post=835"}],"version-history":[{"count":4,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/posts\/835\/revisions"}],"predecessor-version":[{"id":887,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/posts\/835\/revisions\/887"}],"wp:attachment":[{"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/media?parent=835"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/categories?post=835"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/tags?post=835"},{"taxonomy":"course","embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/course?post=835"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}