{"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":1133,"url":"https:\/\/bullyrooks.com\/index.php\/2021\/07\/25\/confirming-the-continuous-deployment-pipeline\/","url_meta":{"origin":835,"position":0},"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":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":1},"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":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":2},"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":3},"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":[]},{"id":837,"url":"https:\/\/bullyrooks.com\/index.php\/2020\/03\/30\/simple-spring-boot-service-to-kubernetes-application-step-8-3e94686c9a34\/","url_meta":{"origin":835,"position":4},"title":"Containerize the Service With Docker","author":"Bullyrook","date":"March 30, 2020","format":false,"excerpt":"Now we\u2019re going to take our functional service and containerize it. This will allow us to control the deployment environment that our java spring boot service runs in as well as provide some additional security and isolation from application updates that can break our application. Containerization Overview If you\u2019ve spent\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":816,"url":"https:\/\/bullyrooks.com\/index.php\/2020\/03\/30\/simple-spring-boot-service-to-kubernetes-application-step-12-c6423261a93a\/","url_meta":{"origin":835,"position":5},"title":"Setting up a Kubernetes Cluster","author":"Bullyrook","date":"March 30, 2020","format":false,"excerpt":"Finally, we\u2019re going to be able to deploy our application. We need to get access to a cluster first. Install Tooling We\u2019re going to need more tools in order to get started. Use your OS package management tool to install these tools: Kubectl (Interact with a k8s instance)Minikube (Run a\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}]}}