{"id":57006,"date":"2023-05-06T09:40:20","date_gmt":"2023-05-06T04:10:20","guid":{"rendered":"https:\/\/www.tothenew.com\/blog\/?p=57006"},"modified":"2024-01-02T17:41:26","modified_gmt":"2024-01-02T12:11:26","slug":"h2-sucks-test-containers-to-rescue","status":"publish","type":"post","link":"https:\/\/www.tothenew.com\/blog\/h2-sucks-test-containers-to-rescue\/","title":{"rendered":"Say No to H2, Yes to Test-Container"},"content":{"rendered":"<p>Developers get confused over the priority of unit test cases and integration test cases. Though both are important, when we are building services for clients, we are on a tight schedule, so completing the development is itself a challenge, let alone writing test cases.<\/p>\n<p id=\"93a5\" class=\"pw-post-body-paragraph qu qv qw qx b qy qz ra rb rc rd re rf rg rh ri rj rk rl rm rn ro rp rq rr rs ok bi\" data-selectable-paragraph=\"\">I always strive to write test cases, irrespective of the time we have. I stress writing Integration test cases over Unit test cases in the first place. In Unit test cases, we are looking to cover a critical piece of code and mock all its surrounding logic. But in production, surrounding matters as much as that piece of code.<\/p>\n<p id=\"6cec\" class=\"pw-post-body-paragraph qu qv qw qx b qy qz ra rb rc rd re rf rg rh ri rj rk rl rm rn ro rp rq rr rs ok bi\" data-selectable-paragraph=\"\">To write Integration tests, we cannot mock repository layers, and we need some mechanism to tackle this. The in-memory H2 database has done the job for years, though very badly. We were never able to replicate the production database for our test cases.<\/p>\n<p data-selectable-paragraph=\"\">It\u2019s very common, that while writing sql queries, we may be using a lot of native MySQL syntax, which H2 may not support. Windows functions or analytical functions which are quite common is not well supported by H2. Support for latest sql features comes late in H2 database. Sometime, I tell my team, try to use standard sql functions over mysql native functions and make sure that those functions are well supported on H2 so that we covered those code snippets in test cases. This is bad. This is not on.<\/p>\n<p data-selectable-paragraph=\"\"><strong class=\"qx eo\">Test Containers to the rescue.<\/strong><\/p>\n<p data-selectable-paragraph=\"\">Now we have test containers for nearly every kind of database with their latest version. So now our test database mimics the production database, and I can take full advantage of the underlying database I am using.<\/p>\n<p id=\"c888\" class=\"pw-post-body-paragraph qu qv qw qx b qy qz ra rb rc rd re rf rg rh ri rj rk rl rm rn ro rp rq rr rs ok bi\" data-selectable-paragraph=\"\">We will now see how easy it is to set up test containers for testing in Spring Boot.<\/p>\n<p id=\"959c\" class=\"pw-post-body-paragraph qu qv qw qx b qy qz ra rb rc rd re rf rg rh ri rj rk rl rm rn ro rp rq rr rs ok bi\" data-selectable-paragraph=\"\">Let\u2019s look into our pom.xml<\/p>\n<pre><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.springframework.boot<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>spring-boot-starter-data-jpa<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.springframework.boot<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>spring-boot-starter-web<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\r\n\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>com.mysql<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>mysql-connector-j<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>runtime<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.projectlombok<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>lombok<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">optional<\/span>&gt;<\/span>true<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">optional<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\r\n<span class=\"hljs-comment\">&lt;!-- main dependencyies ----------&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.springframework.boot<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>spring-boot-starter-test<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>test<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.testcontainers<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>junit-jupiter<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>test<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.testcontainers<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>mysql<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>test<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\r\n<span class=\"hljs-comment\">&lt;!-- main dependencyies ----------&gt;<\/span>\r\n\r\n<span class=\"hljs-comment\">&lt;!-- https:\/\/mvnrepository.com\/artifact\/org.springframework.boot\/spring-boot-starter-webflux --&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.springframework.boot<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>spring-boot-starter-webflux<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version<\/span>&gt;<\/span>3.0.2<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">version<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\r\n\r\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependencies<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependencyManagement<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependencies<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.testcontainers<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>testcontainers-bom<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version<\/span>&gt;<\/span>${testcontainers.version}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">version<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">type<\/span>&gt;<\/span>pom<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">type<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope<\/span>&gt;<\/span>import<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">scope<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependencies<\/span>&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependencyManagement<\/span>&gt;<\/span><\/pre>\n<p id=\"8eb1\" class=\"pw-post-body-paragraph qu qv qw qx b qy qz ra rb rc rd re rf rg rh ri rj rk rl rm rn ro rp rq rr rs ok bi\" data-selectable-paragraph=\"\">Main dependency has been marked in comments.<\/p>\n<p id=\"b7a2\" class=\"pw-post-body-paragraph qu qv qw qx b qy qz ra rb rc rd re rf rg rh ri rj rk rl rm rn ro rp rq rr rs ok bi\" data-selectable-paragraph=\"\">Now consider the\u00a0<strong class=\"qx eo\">OrderEntity.java, OrderEntityRepository.java and OrderController.java class<\/strong><\/p>\n<pre class=\"ru rv rw rx pz sf sg sh bn si sj bi\"><span id=\"fb44\" class=\"sk sl qw sg b be sm sn l so sp\" data-selectable-paragraph=\"\"><span class=\"hljs-keyword\">package<\/span> org.anil.testcontainers.entity;\r\n\r\n<span class=\"hljs-keyword\">import<\/span> jakarta.persistence.*;\r\n<span class=\"hljs-keyword\">import<\/span> lombok.Getter;\r\n<span class=\"hljs-keyword\">import<\/span> lombok.Setter;\r\n\r\n<span class=\"hljs-meta\">@Entity<\/span>\r\n<span class=\"hljs-meta\">@Getter<\/span>\r\n<span class=\"hljs-meta\">@Setter<\/span>\r\n<span class=\"hljs-meta\">@Table(name =\"order_entity\")<\/span>\r\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title.class\">OrderEntity<\/span>{\r\n    <span class=\"hljs-meta\">@Id<\/span>\r\n    <span class=\"hljs-meta\">@GeneratedValue(strategy = GenerationType.IDENTITY)<\/span>\r\n    <span class=\"hljs-keyword\">private<\/span> Long id;\r\n    <span class=\"hljs-keyword\">private<\/span> String orderName;\r\n    <span class=\"hljs-keyword\">private<\/span> Long orderType;\r\n    <span class=\"hljs-meta\">@Transient<\/span>\r\n    <span class=\"hljs-keyword\">private<\/span> String orderTypeDes;\r\n}<\/span><\/pre>\n<pre class=\"la sf sg sh bn si sj bi\"><span id=\"4360\" class=\"sk sl qw sg b be sm sn l so sp\" data-selectable-paragraph=\"\"><span class=\"hljs-keyword\">package<\/span> org.anil.testcontainers.repository;\r\n\r\n<span class=\"hljs-keyword\">import<\/span> org.anil.testcontainers.entity.OrderEntity;\r\n<span class=\"hljs-keyword\">import<\/span> org.springframework.data.jpa.repository.JpaRepository;\r\n\r\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">interface<\/span> <span class=\"hljs-title.class\">OrderEntityRepository<\/span>  <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title.class\">JpaRepository<\/span>&lt;OrderEntity,Long&gt; {\r\n}<\/span><\/pre>\n<pre class=\"la sf sg sh bn si sj bi\"><span id=\"72c5\" class=\"sk sl qw sg b be sm sn l so sp\" data-selectable-paragraph=\"\"><span class=\"hljs-meta\">@RestController<\/span>\r\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title.class\">OrderController<\/span> {\r\n    <span class=\"hljs-meta\">@PersistenceContext<\/span>\r\n    EntityManager entityManager;\r\n\r\n    <span class=\"hljs-meta\">@Autowired<\/span>\r\n    OrderEntityRepository orderEntityRepository;\r\n\r\n    <span class=\"hljs-meta\">@PostMapping(\"\/save\")<\/span>\r\n    <span class=\"hljs-keyword\">public<\/span> ResponseEntity&lt;OrderEntity&gt; <span class=\"hljs-title.function\">saveOrderEntity<\/span><span class=\"hljs-params\">(<span class=\"hljs-meta\">@RequestBody<\/span> OrderEntity orderEntityDto)<\/span>{\r\n       <span class=\"hljs-type\">OrderEntity<\/span> <span class=\"hljs-variable\">orderEntity<\/span> <span class=\"hljs-operator\">=<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-title.class\">OrderEntity<\/span>();\r\n       orderEntity.setOrderName(orderEntityDto.getOrderName());\r\n       orderEntity.setOrderType(orderEntityDto.getOrderType());\r\n        orderEntityRepository.save(orderEntity);\r\n       <span class=\"hljs-keyword\">return<\/span> ResponseEntity.ok(orderEntity);\r\n    }\r\n\r\n    <span class=\"hljs-meta\">@GetMapping(\"\/allorders\")<\/span>\r\n    <span class=\"hljs-keyword\">public<\/span> ResponseEntity&lt;List&lt;OrderEntity&gt;&gt; <span class=\"hljs-title.function\">saveOrderEntity<\/span><span class=\"hljs-params\">()<\/span>{\r\n        <span class=\"hljs-type\">var<\/span> <span class=\"hljs-variable\">query<\/span>  <span class=\"hljs-operator\">=<\/span>entityManager.createNativeQuery(<span class=\"hljs-string\">\"\"\"\r\n                    select id, \r\n                    order_name,\r\n                    order_type,\r\n                    if(order_type = '1','FullFilled','Cancelled') from order_entity ; \r\n            \"\"\"<\/span>);\r\n        <span class=\"hljs-comment\">\/\/ deliberately used if function which is native to mysql<\/span>\r\n        <span class=\"hljs-comment\">\/\/ not available in h2 db.<\/span>\r\n       <span class=\"hljs-type\">var<\/span> <span class=\"hljs-variable\">resultList<\/span> <span class=\"hljs-operator\">=<\/span> (List&lt;Object[]&gt;) query.getResultList();\r\n       List&lt;OrderEntity&gt; list = <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-title.class\">ArrayList<\/span>&lt;&gt;();\r\n       <span class=\"hljs-keyword\">for<\/span>(Object[] object : resultList){\r\n           <span class=\"hljs-type\">var<\/span> <span class=\"hljs-variable\">id<\/span> <span class=\"hljs-operator\">=<\/span> Long.valueOf(object[<span class=\"hljs-number\">0<\/span>].toString());\r\n           <span class=\"hljs-type\">var<\/span> <span class=\"hljs-variable\">orderName<\/span> <span class=\"hljs-operator\">=<\/span> object[<span class=\"hljs-number\">1<\/span>].toString();\r\n           <span class=\"hljs-type\">var<\/span> <span class=\"hljs-variable\">orderType<\/span> <span class=\"hljs-operator\">=<\/span> Long.valueOf(object[<span class=\"hljs-number\">2<\/span>].toString());\r\n           <span class=\"hljs-type\">var<\/span> <span class=\"hljs-variable\">orderTypeDesc<\/span> <span class=\"hljs-operator\">=<\/span> object[<span class=\"hljs-number\">3<\/span>].toString();\r\n           <span class=\"hljs-type\">OrderEntity<\/span> <span class=\"hljs-variable\">orderEntity<\/span> <span class=\"hljs-operator\">=<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-title.class\">OrderEntity<\/span>();\r\n           orderEntity.setId(id);\r\n           orderEntity.setOrderTypeDes(orderTypeDesc);\r\n           orderEntity.setOrderType(orderType);\r\n           list.add(orderEntity);\r\n       }\r\n       <span class=\"hljs-keyword\">return<\/span> ResponseEntity.ok(list);\r\n    }<\/span><\/pre>\n<p id=\"195a\" class=\"pw-post-body-paragraph qu qv qw qx b qy qz ra rb rc rd re rf rg rh ri rj rk rl rm rn ro rp rq rr rs ok bi\" data-selectable-paragraph=\"\">We would be writing integration test cases for the above Controller\u2019s methods.<\/p>\n<p id=\"a1d2\" class=\"pw-post-body-paragraph qu qv qw qx b qy qz ra rb rc rd re rf rg rh ri rj rk rl rm rn ro rp rq rr rs ok bi\" data-selectable-paragraph=\"\">In OrderControllerTest.java we will first configure our MySQL test container.<\/p>\n<pre class=\"ru rv rw rx pz sf sg sh bn si sj bi\"><span id=\"bf15\" class=\"sk sl qw sg b be sm sn l so sp\" data-selectable-paragraph=\"\"><span class=\"hljs-meta\">@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)<\/span>\r\n<span class=\"hljs-meta\">@AutoConfigureWebTestClient<\/span>\r\n<span class=\"hljs-meta\">@Testcontainers<\/span>\r\n<span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title.class\">OrderControllerTest<\/span> {\r\n\r\n    <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">final<\/span> MySQLContainer MY_SQL_CONTAINER;\r\n\r\n    <span class=\"hljs-meta\">@Autowired<\/span>\r\n    WebTestClient webTestClient;\r\n\r\n    <span class=\"hljs-meta\">@Autowired<\/span>\r\n    <span class=\"hljs-keyword\">private<\/span> OrderEntityRepository  orderEntityRepository;\r\n\r\n    <span class=\"hljs-keyword\">static<\/span> {\r\n        MY_SQL_CONTAINER = <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-title.class\">MySQLContainer<\/span>(<span class=\"hljs-string\">\"mysql:latest\"<\/span>);\r\n        MY_SQL_CONTAINER.start();\r\n    }<\/span><\/pre>\n<p id=\"03e6\" class=\"pw-post-body-paragraph qu qv qw qx b qy qz ra rb rc rd re rf rg rh ri rj rk rl rm rn ro rp rq rr rs ok bi\" data-selectable-paragraph=\"\">We need first to annotate our Test class with @Testcontainers, and then in the static block, we have to create a new MySQLContainer with the latest MySQL Docker image. For the first time, it will pull the MySQL image with the tag \u201clatest.\u201d Docker should be installed on your system to work with containers.<\/p>\n<p id=\"4585\" class=\"pw-post-body-paragraph qu qv qw qx b qy qz ra rb rc rd re rf rg rh ri rj rk rl rm rn ro rp rq rr rs ok bi\" data-selectable-paragraph=\"\">And then, we start the MySQL container with a start () method. When the test cases are finished, this MySQL container will be stopped and removed from Docker\u2019s list. Just like in-memory H2, the container will be up and tear down on the test exit.<\/p>\n<p id=\"319a\" class=\"pw-post-body-paragraph qu qv qw qx b qy qz ra rb rc rd re rf rg rh ri rj rk rl rm rn ro rp rq rr rs ok bi\" data-selectable-paragraph=\"\">We need to tell Spring Boot about the properties of this database for the test container, and Spring has a nice way to do this.<\/p>\n<pre class=\"ru rv rw rx pz sf sg sh bn si sj bi\"><span id=\"af75\" class=\"sk sl qw sg b be sm sn l so sp\" data-selectable-paragraph=\"\"><span class=\"hljs-meta\">@DynamicPropertySource<\/span>\r\n    <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title.function\">configureTestProperties<\/span><span class=\"hljs-params\">(DynamicPropertyRegistry registry)<\/span>{\r\n        registry.add(<span class=\"hljs-string\">\"spring.datasource.url\"<\/span>,() -&gt; MY_SQL_CONTAINER.getJdbcUrl());\r\n        registry.add(<span class=\"hljs-string\">\"spring.datasource.username\"<\/span>,() -&gt; MY_SQL_CONTAINER.getUsername());\r\n        registry.add(<span class=\"hljs-string\">\"spring.datasource.password\"<\/span>,() -&gt; MY_SQL_CONTAINER.getPassword());\r\n        registry.add(<span class=\"hljs-string\">\"spring.jpa.hibernate.ddl-auto\"<\/span>,() -&gt; <span class=\"hljs-string\">\"create\"<\/span>);\r\n\r\n    }<\/span><\/pre>\n<p id=\"6171\" class=\"pw-post-body-paragraph qu qv qw qx b qy qz ra rb rc rd re rf rg rh ri rj rk rl rm rn ro rp rq rr rs ok bi\" data-selectable-paragraph=\"\">Let\u2019s setup the BeforeEach and AfterEach for this test case.<\/p>\n<pre class=\"ru rv rw rx pz sf sg sh bn si sj bi\"><span id=\"3a03\" class=\"sk sl qw sg b be sm sn l so sp\" data-selectable-paragraph=\"\"> <span class=\"hljs-meta\">@BeforeEach<\/span>\r\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title.function\">beforeEach<\/span><span class=\"hljs-params\">()<\/span>{\r\n        <span class=\"hljs-type\">OrderEntity<\/span> <span class=\"hljs-variable\">orderEntity<\/span> <span class=\"hljs-operator\">=<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-title.class\">OrderEntity<\/span>();\r\n        orderEntity.setOrderType(<span class=\"hljs-number\">1L<\/span>);\r\n        orderEntity.setOrderName(<span class=\"hljs-string\">\"order-1\"<\/span>);\r\n        orderEntityRepository.save(orderEntity);\r\n    }\r\n    <span class=\"hljs-meta\">@AfterEach<\/span>\r\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title.function\">afterEach<\/span><span class=\"hljs-params\">()<\/span>{\r\n        orderEntityRepository.deleteAll();\r\n    }<\/span><\/pre>\n<p id=\"6a0c\" class=\"pw-post-body-paragraph qu qv qw qx b qy qz ra rb rc rd re rf rg rh ri rj rk rl rm rn ro rp rq rr rs ok bi\" data-selectable-paragraph=\"\">Let&#8217;s write the test case for the save method<\/p>\n<pre class=\"ru rv rw rx pz sf sg sh bn si sj bi\"><span id=\"4932\" class=\"sk sl qw sg b be sm sn l so sp\" data-selectable-paragraph=\"\"><span class=\"hljs-meta\">@Test<\/span>\r\n    <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title.function\">saveOrderEntity<\/span><span class=\"hljs-params\">()<\/span> {\r\n        <span class=\"hljs-type\">OrderEntity<\/span> <span class=\"hljs-variable\">orderEntity<\/span> <span class=\"hljs-operator\">=<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-title.class\">OrderEntity<\/span>();\r\n        orderEntity.setOrderType(<span class=\"hljs-number\">1L<\/span>);\r\n        orderEntity.setOrderName(<span class=\"hljs-string\">\"order-2\"<\/span>);\r\n        webTestClient.post()\r\n                .uri(<span class=\"hljs-string\">\"\/save\"<\/span>)\r\n                .bodyValue(orderEntity)\r\n                .exchange()\r\n                .expectHeader()\r\n                .contentType(MediaType.APPLICATION_JSON)\r\n                .expectStatus()\r\n                .is2xxSuccessful()\r\n                .expectBody(OrderEntity.class)\r\n                .consumeWith(orderentity -&gt; Assertions.assertNotNull(orderentity.getResponseBody().getId()));\r\n    }<\/span><\/pre>\n<p id=\"74fc\" class=\"pw-post-body-paragraph qu qv qw qx b qy qz ra rb rc rd re rf rg rh ri rj rk rl rm rn ro rp rq rr rs ok bi\" data-selectable-paragraph=\"\">And now for get method<\/p>\n<pre class=\"ru rv rw rx pz sf sg sh bn si sj bi\"><span id=\"67f3\" class=\"sk sl qw sg b be sm sn l so sp\" data-selectable-paragraph=\"\">  <span class=\"hljs-meta\">@Test<\/span>\r\n    <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title.function\">getOrderEntity<\/span><span class=\"hljs-params\">()<\/span> {\r\n        webTestClient.get()\r\n                .uri(<span class=\"hljs-string\">\"\/allorders\"<\/span>)\r\n                .exchange()\r\n                .expectHeader()\r\n                .contentType(MediaType.APPLICATION_JSON)\r\n                .expectStatus()\r\n                .is2xxSuccessful()\r\n                .expectBodyList(OrderEntity.class)\r\n                .consumeWith(listOfObject -&gt;{\r\n                   <span class=\"hljs-type\">var<\/span> <span class=\"hljs-variable\">list<\/span>  <span class=\"hljs-operator\">=<\/span> listOfObject.getResponseBody();\r\n                    Assertions.assertTrue(list.size() == <span class=\"hljs-number\">1<\/span>);\r\n                    Assertions.assertTrue(list.get(<span class=\"hljs-number\">0<\/span>).getOrderTypeDes().equals(<span class=\"hljs-string\">\"FullFilled\"<\/span>));\r\n                });\r\n    }<\/span><\/pre>\n<p id=\"c2ad\" class=\"pw-post-body-paragraph qu qv qw qx b qy qz ra rb rc rd re rf rg rh ri rj rk rl rm rn ro rp rq rr rs ok bi\" data-selectable-paragraph=\"\">When we run the test cases, the MySQL image will be pulled when running for the first time, and lots of logs will be generated. Test containers have provided recommended logback.xml to get rid of verbosity.<\/p>\n<p id=\"b465\" class=\"pw-post-body-paragraph qu qv qw qx b qy qz ra rb rc rd re rf rg rh ri rj rk rl rm rn ro rp rq rr rs ok bi\" data-selectable-paragraph=\"\">Here is the logback-test.xml that can be included in the resource folder.<\/p>\n<pre class=\"ru rv rw rx pz sf sg sh bn si sj bi\"><span id=\"7e46\" class=\"sk sl qw sg b be sm sn l so sp\" data-selectable-paragraph=\"\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">configuration<\/span>&gt;<\/span>\r\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">appender<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"STDOUT\"<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ch.qos.logback.core.ConsoleAppender\"<\/span>&gt;<\/span>\r\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">encoder<\/span>&gt;<\/span>\r\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">pattern<\/span>&gt;<\/span>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">pattern<\/span>&gt;<\/span>\r\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">encoder<\/span>&gt;<\/span>\r\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">appender<\/span>&gt;<\/span>\r\n\r\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">root<\/span> <span class=\"hljs-attr\">level<\/span>=<span class=\"hljs-string\">\"info\"<\/span>&gt;<\/span>\r\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">appender-ref<\/span> <span class=\"hljs-attr\">ref<\/span>=<span class=\"hljs-string\">\"STDOUT\"<\/span>\/&gt;<\/span>\r\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">root<\/span>&gt;<\/span>\r\n\r\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">logger<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"org.testcontainers\"<\/span> <span class=\"hljs-attr\">level<\/span>=<span class=\"hljs-string\">\"INFO\"<\/span>\/&gt;<\/span>\r\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">logger<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"com.github.dockerjava\"<\/span> <span class=\"hljs-attr\">level<\/span>=<span class=\"hljs-string\">\"WARN\"<\/span>\/&gt;<\/span>\r\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">logger<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"com.github.dockerjava.zerodep.shaded.org.apache.hc.client5.http.wire\"<\/span> <span class=\"hljs-attr\">level<\/span>=<span class=\"hljs-string\">\"OFF\"<\/span>\/&gt;<\/span>\r\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">configuration<\/span>&gt;<\/span><\/span><\/pre>\n<p id=\"464f\" class=\"pw-post-body-paragraph qu qv qw qx b qy qz ra rb rc rd re rf rg rh ri rj rk rl rm rn ro rp rq rr rs ok bi\" data-selectable-paragraph=\"\">We can see that the MySQL test container has started. We can see the Docker whale icon as well.<\/p>\n<figure class=\"ru rv rw rx pz ry po el paragraph-image\">\n<div class=\"rz sa cl sb bf sc\" role=\"button\">\n<div class=\"po el ajt\"><img decoding=\"async\" loading=\"lazy\" class=\"bf sd se c\" role=\"presentation\" src=\"\/blog\/wp-ttn-blog\/uploads\/2024\/01\/1fSBOtBKqlvTK4wicD1zfOg.png\" alt=\"\" width=\"700\" height=\"321\" \/><\/div>\n<\/div>\n<\/figure>\n<p id=\"f7c3\" class=\"pw-post-body-paragraph qu qv qw qx b qy qz ra rb rc rd re rf rg rh ri rj rk rl rm rn ro rp rq rr rs ok bi\" data-selectable-paragraph=\"\">I like test containers a lot, as I write great test cases that mimic my production environment. I am quite confident when my code goes into production.<\/p>\n<p id=\"5544\" class=\"pw-post-body-paragraph qu qv qw qx b qy qz ra rb rc rd re rf rg rh ri rj rk rl rm rn ro rp rq rr rs ok bi\" data-selectable-paragraph=\"\">Please find the code on my GitHub link: https:\/\/github.com\/anilgola90\/testcontainers<\/p>\n<p data-selectable-paragraph=\"\">Feel free to ask your questions in the comment section below.<\/p>\n<div class=\"ap-custom-wrapper\"><\/div><!--ap-custom-wrapper-->","protected":false},"excerpt":{"rendered":"<p>Developers get confused over the priority of unit test cases and integration test cases. Though both are important, when we are building services for clients, we are on a tight schedule, so completing the development is itself a challenge, let alone writing test cases. I always strive to write test cases, irrespective of the time [&hellip;]<\/p>\n","protected":false},"author":1520,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":136},"categories":[446],"tags":[5157,5158],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/57006"}],"collection":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/users\/1520"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/comments?post=57006"}],"version-history":[{"count":7,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/57006\/revisions"}],"predecessor-version":[{"id":59697,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/57006\/revisions\/59697"}],"wp:attachment":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/media?parent=57006"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/categories?post=57006"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/tags?post=57006"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}