<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Philip Riecks</title>
    <description>The latest articles on Forem by Philip Riecks (@rieckpil).</description>
    <link>https://forem.com/rieckpil</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F183224%2Fc69df887-2fcc-4066-b80f-f543dfd1e544.jpg</url>
      <title>Forem: Philip Riecks</title>
      <link>https://forem.com/rieckpil</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/rieckpil"/>
    <language>en</language>
    <item>
      <title>Remote Java Developer Technical Hardware and Software Setup</title>
      <dc:creator>Philip Riecks</dc:creator>
      <pubDate>Fri, 03 Jun 2022 08:15:18 +0000</pubDate>
      <link>https://forem.com/rieckpil/remote-java-developer-technical-hardware-and-software-setup-4lpn</link>
      <guid>https://forem.com/rieckpil/remote-java-developer-technical-hardware-and-software-setup-4lpn</guid>
      <description>&lt;p&gt;This article describes my working setup (hardware and software) as a remote freelance Java developer. I've been working from either home, co-working places, or cafes for almost two years. That's why the setup is split into two working modes: working from home and working mobile in a co-working place. On top of the hardware setup, this guide includes the software I'm using on a daily basis.&lt;/p&gt;

&lt;p&gt;Disclaimer: The marked links* are affiliate links. If you buy through those links, I get a small cut. I hope that's fine, as I've invested a lot of time and money to find this (at least for me) perfect setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Java Developer Work from Home Hardware Setup
&lt;/h2&gt;

&lt;p&gt;Let's start with the hardware setup I'm using when working from home.&lt;/p&gt;

&lt;p&gt;I'm working and developing software on a Mac Book Pro M1 2021 16GB Ram 13,3 Inc 512 GB laptop with the touch bar. In the past, I've frequently switched between using Windows, Linux (Ubuntu), and Mac. But I've now stuck to Mac as the tooling support, as well as the integration to my tablet and mobile phone, is perfect. While the price is enormous compared to what you get when buying a similar Windows laptop, I can deduct these expenses as I'm a freelancer.&lt;/p&gt;

&lt;p&gt;I went for the 13,3-inch screen size because I frequently work from co-working places or travel by train. A smaller device is better in such situations. After &lt;a href="https://rieckpil.de/java-development-on-an-apple-m1-a-one-year-review/"&gt;solving some initial hurdles as a Java developer working with an M1 chip&lt;/a&gt; (arm64), I'm now really productive with this laptop, and it's super fast and slim (good for travel).&lt;/p&gt;

&lt;p&gt;The MacBook is connected to a docking station (Raidsonic ICY BOX IB-DK2245AC) via USB-C and to an external monitor. As I'm frequently changing workplaces, I want a smooth transition without many cables resorting. I can just remove the docking station cable and put the laptop into my bag.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CzI1qC7F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rieckpil.de/wp-content/uploads/2022/05/work-from-home-java-developer-setup-docking-station-view-768x1024.jpg.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CzI1qC7F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rieckpil.de/wp-content/uploads/2022/05/work-from-home-java-developer-setup-docking-station-view-768x1024.jpg.webp" alt="Docking Station View" title="Docking Station View" width="768" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The screen is a &lt;a href="https://geni.us/kJPyay"&gt;DELL S2721QS (27″) 4K TFT&lt;/a&gt;*. I'm fine with having two screens in total: one external monitor and the integrated Mac screen. While the monitor allows for 4k resolution, I usually use a 1920×1080 resolution.&lt;/p&gt;

&lt;p&gt;The laptop stand for my WFH setup is a solid non-adjustable: &lt;a href="https://geni.us/OLmw"&gt;Rain Design mStand&lt;/a&gt;*. To organize my desk, I'm using an IKEA stand (ELLOVEN) for the external monitor. With this stand, I can additionally store some pencils, paper, cables, and USB sticks.&lt;/p&gt;

&lt;p&gt;All this hardware is placed on a 200x80cm height-adjustable desk from IKEA (BEKANT):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vzcfvD0P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rieckpil.de/wp-content/uploads/2022/05/work-from-home-java-developer-setup-view-full-1536x1152.jpg.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vzcfvD0P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rieckpil.de/wp-content/uploads/2022/05/work-from-home-java-developer-setup-view-full-1536x1152.jpg.webp" alt="Desk View" title="Desk View" width="880" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I try to balance the time sitting and standing every day. There's no better investment than investing in your health and work setup that will last.&lt;/p&gt;

&lt;p&gt;The chair is also from IKEA (LÅNGFJÄLL) and is both stylish and comfortable.&lt;/p&gt;

&lt;p&gt;As I'm regularly organizing and scanning receipts, I've bought the &lt;a href="https://geni.us/Rbeixw"&gt;Brother MFC-L2750DW&lt;/a&gt;*. This printer works with AirPrint (Apple's printing solution) and has some built-in apps to scan a document, convert it to PDF and directly upload it to a OneDrive folder.&lt;/p&gt;

&lt;p&gt;As a fallback solution, I still have a desktop machine. This is an Ubuntu 20.04 with 16 GB RAM and an i7. If my main laptop has issues, I can easily switch to another machine. On top of this, I sometimes use the Ubuntu machine if there's no compatibility with the Apple M1 chips for any enterprise software of my clients.&lt;/p&gt;

&lt;p&gt;This article is written on an &lt;a href="https://geni.us/NUCJl"&gt;Apple magic keyboard&lt;/a&gt;* while using the &lt;a href="https://geni.us/BHz7"&gt;Logitech MX Master 3&lt;/a&gt;* mouse. I'm not into those mechanical keyboards and enjoy the slim design of Apple's keyboard.&lt;/p&gt;

&lt;p&gt;I started with the &lt;a href="https://geni.us/g1K6fo"&gt;Logitech MX Keys keyboard&lt;/a&gt;* but switched to the Apple one. The Logitech MX Keys keyboard is still used for working with my Ubuntu machine. Both the MX keyboard and mouse have a neat feature to connect to up to three devices and switch the current machine context with just a click.&lt;/p&gt;

&lt;p&gt;For meetings and calls, I'm using a Jabra Evolve 75 headset. For sharing a picture of myself in those calls, I'm either using the built-in camera of the MacBook Pro or a &lt;a href="https://geni.us/cmXVsIu"&gt;cheap 1080p camera&lt;/a&gt;*. This USB camera does its job well, but I might soon upgrade to a new one.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://geni.us/cy21Ee"&gt;underlying matt&lt;/a&gt;* is from Amazon, and it helps me to keep the desk surface protected and works perfectly with the mouse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Java Developer Mobile Co-Working Hardware Setup
&lt;/h2&gt;

&lt;p&gt;When I'm out of home, I slightly change the hardware setup. The co-working spaces and cafes usually don't offer any external monitors. That's why I have to improvise and find a good balance between carrying too much hardware with me and having a healthy setup.&lt;/p&gt;

&lt;p&gt;I've recently bought the &lt;a href="https://geni.us/YsXK7NB"&gt;Nextstand V2 laptop stand&lt;/a&gt;*. This allows me to have good posture as I'm looking up straight into the laptop and not down. The downside of using a laptop stand is that you have to carry a keyboard and mouse; otherwise, typing will be hard.&lt;/p&gt;

&lt;p&gt;The Apple magic keyboard and the Logitech MX Master 3 mouse are both lightweight, comfortable, and easy to travel with:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Gdz8U5NT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rieckpil.de/wp-content/uploads/2022/05/work-from-remote-java-developer-coworking-setup-768x576.jpg.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Gdz8U5NT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rieckpil.de/wp-content/uploads/2022/05/work-from-remote-java-developer-coworking-setup-768x576.jpg.webp" alt="Mobile Setup View" title="Mobile Setup View" width="768" height="576"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If I need a second monitor, I connect my iPad Pro 11″ as an external screen for my Mac Book Pro. The Mac comes with a built-in feature for this.&lt;/p&gt;

&lt;p&gt;I'm using either the &lt;a href="https://geni.us/V9AH"&gt;Apple AirPods Pro&lt;/a&gt;* or the regular Apple headphones for calls and listening to music. Most co-working places offer phone booths that ensure there's no background noise.&lt;/p&gt;

&lt;p&gt;Some additional accessories I'm using for this mobile working setup are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://geni.us/ZMJ7OI"&gt;Laptop case&lt;/a&gt;*&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://geni.us/DynxYJ"&gt;Travel case for the keyboard&lt;/a&gt;*&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://geni.us/bZKaAz"&gt;USB-C to USB-A adapters&lt;/a&gt;*&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When traveling, I switch between these three bags:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JOOP! Treviso Pandion Briefbag S Black&lt;/li&gt;
&lt;li&gt;Backpack from onemate: &lt;a href="https://onemate.de/"&gt;Discovery&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Brown laptop bag from s.Oliver&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Online Course Recording Setup (Hardware &amp;amp; Software)
&lt;/h2&gt;

&lt;p&gt;As I'm creating video content for both &lt;a href="https://www.youtube.com/c/rieckpil"&gt;YouTube&lt;/a&gt; and my &lt;a href="https://rieckpil.de/courses/"&gt;online course platform&lt;/a&gt;, I'm recording audio every now and then. I started with an external mic, as many streamers or podcasters have. This microphone was attached to a microphone stand and attached to my desk.&lt;/p&gt;

&lt;p&gt;While this worked fine, I recently switched to a headset for my recordings. For me, this feels handier. I only have to switch on the headset and can start the recording. No need for an adjustment of the microphone, and I can move back and forth in my chair without affecting the sound quality.&lt;/p&gt;

&lt;p&gt;The headset I'm using is a beyerdynamic DT-797:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TVG0IcYk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rieckpil.de/wp-content/uploads/2022/05/work-from-home-java-developer-setup-recording-setup-768x1024.jpg.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TVG0IcYk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rieckpil.de/wp-content/uploads/2022/05/work-from-home-java-developer-setup-recording-setup-768x1024.jpg.webp" alt="Headset View" title="Headset View" width="768" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, I can't wear this for more than 2 hours straight because of the pressure on my head. The headset rests safely on a stylish &lt;a href="https://geni.us/P2oAX"&gt;headphone stand&lt;/a&gt;* from Amazon.&lt;/p&gt;

&lt;p&gt;To convert the analog audio signal from this beyerdynamic headset, I'm using a &lt;a href="https://geni.us/aIJT5"&gt;Focusrite Scarlett 2i2 3rd Gen Audio Interface&lt;/a&gt;*. This lets me plug in the headset via USB-C to my docking station.&lt;/p&gt;

&lt;p&gt;I'm using &lt;a href="https://www.telestream.net/screenflow/overview.htm"&gt;Screenflow&lt;/a&gt; to record and edit all the content I create on the Mac laptop. When working with my fallback Ubuntu Desktop for recording, I'm using Kazam, Audacity, and kdenlive.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bWGgDpVv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rieckpil.de/wp-content/uploads/2022/05/work-from-home-java-developer-setup-view-bottom-768x1024.jpg.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bWGgDpVv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://rieckpil.de/wp-content/uploads/2022/05/work-from-home-java-developer-setup-view-bottom-768x1024.jpg.webp" alt="Full Station View" title="Full Station View" width="768" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Software for Java Development
&lt;/h2&gt;

&lt;p&gt;Let's continue with the software part of my setup as a Java developer.&lt;/p&gt;

&lt;p&gt;The application I spent the most time during the day is my IDE. I'm using &lt;a href="https://www.jetbrains.com/idea/"&gt;IntelliJ IDEA&lt;/a&gt; with the following must-have plugins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AsciiDoc (as I'm writing some of my ebooks in Asciidoc)&lt;/li&gt;
&lt;li&gt;Presentation Assistant to show a toast notification whenever I use shortcuts&lt;/li&gt;
&lt;li&gt;SonarLint for basic code hints&lt;/li&gt;
&lt;li&gt;Key Promoter X to learn more IDEA shortcuts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I switched from Eclipse to IntelliJ five years ago and have never looked back since. I'm using their Ultimate license and really enjoy the productivity gain and features of this IDE.&lt;/p&gt;

&lt;p&gt;I use jEnv to manage various Java installations (I guess it's time for me to get to know sdkman) and &lt;a href="https://www.youtube.com/watch?v=9FVZyeFDXo0"&gt;easily switch between Java versions&lt;/a&gt;. With jEnv, you can place &lt;code&gt;.java-version&lt;/code&gt; file in the root of each project and define which Java version to use. When navigating to a folder with such file, jEnv configures your shell session to use the selected Java version. It falls back to a globally configured Java version if no Java version is defined.&lt;/p&gt;

&lt;p&gt;For the shell, I'm using &lt;code&gt;zsh&lt;/code&gt; with &lt;a href="https://ohmyz.sh/"&gt;Oh My Zsh&lt;/a&gt;. I'm using the default theme &lt;code&gt;robbyrussell&lt;/code&gt;, and the following plugins: &lt;code&gt;git&lt;/code&gt;, &lt;code&gt;zsh-autosuggestions&lt;/code&gt;, &lt;code&gt;zsh-syntax-highlighting&lt;/code&gt; and &lt;code&gt;history&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I've installed &lt;a href="https://iterm2.com/"&gt;iTerm2&lt;/a&gt; as a replacement for the macOS native terminal. The main reason is the split pane feature that allows me to run multiple shell sessions on a single screen.&lt;/p&gt;

&lt;p&gt;When editing files on a remote server, I prefer vim over emacs. However, I'm not a vim ninja, but I feel comfortable editing config files with it.&lt;/p&gt;

&lt;p&gt;For quick project investigations or when writing blog posts in Markdown, I prefer &lt;a href="https://code.visualstudio.com/"&gt;VS Code&lt;/a&gt;. I have installed the following extensions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;Extension Pack for Java&lt;/li&gt;
&lt;li&gt;Debugger for Java&lt;/li&gt;
&lt;li&gt;Language Support for Java by Red Hat&lt;/li&gt;
&lt;li&gt;Maven for Java&lt;/li&gt;
&lt;li&gt;Intellij IDEA Keybindings&lt;/li&gt;
&lt;li&gt;Grammarly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, I'm using Postman for tinkering and exploring the API for systems that I'm about to integrate. I've created an account on their site to share my API collections among multiple machines potentially.&lt;/p&gt;

&lt;p&gt;Last but not least, I've installed Docker Desktop for Mac. As I'm a sole trader, I don't fall into Docker's new licensing agreement and can continue to use their desktop installation for Mac. However, as one of the main reasons for me to use Docker locally is to run tests with Testcontainers, I'm using &lt;a href="https://www.atomicjar.com/2021/11/announcing-testcontainers-cloud/"&gt;Testcontainers Cloud&lt;/a&gt; whenever I can. This tool lets me run my Testcontainers integration test locally without the need for a Docker engine, and it speeds things up when I'm on bad WiFi.&lt;/p&gt;

&lt;h2&gt;
  
  
  Java Developer Utility &amp;amp; Productivity Software
&lt;/h2&gt;

&lt;p&gt;Let's finish this hardware and software guide for Java developers with some utility and productivity software I'm using on a daily basis. Some of them are not directly related to software development. However, they help me stay productive or organize my workday.&lt;/p&gt;

&lt;p&gt;First, my primary browser is Firefox. I'm using Firefox also on my mobile devices and syncing my bookmarks to, e.g., read interesting articles on a train ride after having bookmarked them on my laptop. I use Chrome and Safari less frequently and mainly for testing purposes of my blog and applications.&lt;/p&gt;

&lt;p&gt;For messaging and coordinating in a remote and asynchronous world, I favor Slack. If I'm not working for a client, I never have Slack open all the time and instead open it every two or three hours to stay focused on my current task.&lt;/p&gt;

&lt;p&gt;I'm using the Google Workspace to organize my solo company and use the Gmail web view for managing emails.&lt;/p&gt;

&lt;p&gt;On top of Google Workspace, I still have an Office365 subscription. The main reason for this subscription is the fact that I can share the office applications with up to five people and get 1 TB of OneDrive cloud storage. I use OneDrive for most of my personal files.&lt;/p&gt;

&lt;p&gt;To quickly adjust and split any application window, I use &lt;a href="https://rectangleapp.com/"&gt;Rectangle&lt;/a&gt;. This simple application allows adjusting the sizing of windows and quickly placing two next together, for example.&lt;/p&gt;

&lt;p&gt;Another small and super helpful macOS tool is &lt;a href="https://maccy.app/"&gt;Maccy&lt;/a&gt;. This clipboard manager on steroids keeps a history of all my clipboards and lets me efficiently use a previous one.&lt;/p&gt;

&lt;p&gt;To quickly record my screen or share screenshots, I'm using &lt;a href="https://rieckpil.de/a/cloudapp"&gt;CloudApp&lt;/a&gt;*. Once you finish the recording or take a screenshot, the tool will immediately upload the content, and you get a sharable public URL. This is helpful when working with a support team to share error reports.&lt;/p&gt;

&lt;p&gt;For all my personal notes, checklists, and book summaries, I'm using &lt;a href="https://www.notion.so/"&gt;Notion&lt;/a&gt;. Notion lets you organize your content with Markdown-based files and offers an intuitive and fast user interface natively, in the browser and on mobile devices.&lt;/p&gt;

&lt;p&gt;Spotify is another application that I immediately install when setting up a new machine. Listening to music helps me get into the tunnel and stay focussed. For deep work and when doing creative tasks, I prefer piano music. For all other tasks, I frequently mix the music genre.&lt;/p&gt;

&lt;p&gt;All my passwords are stored within the password manager &lt;a href="https://rieckpil.de/a/dashlane"&gt;Dashlane&lt;/a&gt;*. Dashlane comes with a browser extension for every major browser and also seamlessly integrates with the iOS password manager. The times of noting down my password with pen and paper are over. With the help of a password manager, you can max out the security requirements for all passwords as you don't have to remember them.&lt;/p&gt;

&lt;p&gt;For organizing my workday and task planning, I use &lt;a href="https://kanbanflow.com/"&gt;Kanbanflow&lt;/a&gt;. I plan all my days ahead and split the tasks into 25-minutes pomodori (&lt;a href="https://todoist.com/de/productivity-methods/pomodoro-technique"&gt;Pomodoro Technique&lt;/a&gt;) chunks. The tool runs as a web application in the browser and comes with a Pomodoro timer that I use to know when it's time to work and when it's time for a break.&lt;/p&gt;

&lt;p&gt;Finally, I use both Grammarly and DeepL for translations and proofreading.&lt;/p&gt;

&lt;p&gt;Joyful testing,&lt;/p&gt;

&lt;p&gt;Philip&lt;/p&gt;

</description>
      <category>java</category>
      <category>remote</category>
      <category>freelance</category>
      <category>hardware</category>
    </item>
    <item>
      <title>Write Spring Boot integration tests with Testcontainers (JUnit 4 &amp; 5)</title>
      <dc:creator>Philip Riecks</dc:creator>
      <pubDate>Thu, 02 Jul 2020 17:19:15 +0000</pubDate>
      <link>https://forem.com/rieckpil/write-spring-boot-integration-tests-with-testcontainers-junit-4-5-40am</link>
      <guid>https://forem.com/rieckpil/write-spring-boot-integration-tests-with-testcontainers-junit-4-5-40am</guid>
      <description>&lt;p&gt;Recently, I was looking for a solution to write integration tests for my Spring Boot-based application which was using PostgreSQL. I had the following requirements for this task:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; The integration tests should use the &lt;strong&gt;same database&lt;/strong&gt; as in production (referring to the &lt;a href="https://12factor.net/"&gt;Twelve-Factor App&lt;/a&gt; I wanted to keep my environment during the tests as similar as possible to the production environment)&lt;/li&gt;
&lt;li&gt; The tests should &lt;strong&gt;not need any pre-setup&lt;/strong&gt; before running (e.g. like manually setting up a test database)&lt;/li&gt;
&lt;li&gt; The tests should use my &lt;strong&gt;Flyway DDL&lt;/strong&gt; scripts and &lt;strong&gt;create-drop&lt;/strong&gt; (&lt;code&gt;spring.jpa.hibernate.ddl-auto&lt;/code&gt;) shouldn't be activated for my tests&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Good integration&lt;/strong&gt; with the excellent Spring tests ecosystem&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For this task, I found the awesome project: &lt;a href="https://www.testcontainers.org/"&gt;Test containers&lt;/a&gt;. The project describes itself as the following:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Testcontainers is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With this blog post, we'll use Testcontainers to write integration tests with JUnit using a &lt;em&gt;real&lt;/em&gt; database (meaning not mocked or in-memory) for a &lt;a href="https://github.com/rieckpil/blog-tutorials/tree/master/spring-boot-integration-tests-testcontainers"&gt;Spring Boot&lt;/a&gt; application. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPDATE&lt;/strong&gt;: Time flies and a lot was introduced since I published this blog post. Therefore I added integration test examples for different combinations of JUnit 4 &amp;amp; 5 and Spring Boot versions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup Testcontainers in Spring Boot project
&lt;/h2&gt;

&lt;p&gt;For using this dependency you need to have Docker on your local machine/on your build server (Jenkins etc.).&lt;/p&gt;

&lt;p&gt;With Testcontainers you can use a &lt;code&gt;@ClassRule&lt;/code&gt; or &lt;code&gt;@Rule&lt;/code&gt; on each of your integration tests and define the Docker image for your test (valid for JUnit 4.12). &lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;MySQL&lt;/strong&gt; and &lt;strong&gt;PostgreSQL&lt;/strong&gt; and there are already built-in solutions but you are free to use an image of your choice like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// generic container for self-defined Docker images&lt;/span&gt;
&lt;span class="nd"&gt;@ClassRule&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;GenericContainer&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GenericContainer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"redis:3.0.6"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;withExposedPorts&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6379&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// built-in containers&lt;/span&gt;
&lt;span class="nd"&gt;@ClassRule&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;PostgreSQLContainer&lt;/span&gt; &lt;span class="n"&gt;postgreSQLContainer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PostgreSQLContainer&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;withPassword&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"inmemory"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withUsername&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"inmemory"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To run the integrations tests after your unit tests, simply add &lt;code&gt;maven-failsafe-plugin&lt;/code&gt; to your project. In addition, make sure your integration tests have &lt;code&gt;IT&lt;/code&gt; as a postfix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;build&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.maven.plugins&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-failsafe-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.0.0-M4&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;integration-test&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;verify&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/build&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Basic application integration test with Testcontainers
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Using: JUnit 4.12 and Spring Boot &amp;lt; 2.2.6&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let's start with the integration test each Spring Boot application contains out-of-the-box. This integration test verifies that Spring can create the context and start the application. &lt;/p&gt;

&lt;p&gt;As our application requires a PostgreSQL to be available during startup, we can provide one using Testcontainers. Overriding the properties to use the PostgreSQL database spawned by Testcontainers is as easy as the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@RunWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SpringRunner&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@SpringBootTest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;webEnvironment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SpringBootTest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;WebEnvironment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RANDOM_PORT&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@ContextConfiguration&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initializers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;IntegrationTest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Initializer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationIT&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@ClassRule&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;PostgreSQLContainer&lt;/span&gt; &lt;span class="n"&gt;postgreSQLContainer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PostgreSQLContainer&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;withPassword&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"inmemory"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withUsername&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"inmemory"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Initializer&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ApplicationContextInitializer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ConfigurableApplicationContext&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="nd"&gt;@Override&lt;/span&gt;
        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ConfigurableApplicationContext&lt;/span&gt; &lt;span class="n"&gt;configurableApplicationContext&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;TestPropertyValues&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TestPropertyValues&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                    &lt;span class="s"&gt;"spring.datasource.url="&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;postgreSQLContainer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getJdbcUrl&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                    &lt;span class="s"&gt;"spring.datasource.password="&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;postgreSQLContainer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPassword&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                    &lt;span class="s"&gt;"spring.datasource.username="&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;postgreSQLContainer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUsername&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;applyTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configurableApplicationContext&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Ŧest&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;contextLoads&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Basic application integration test with JUnit 5 and Spring Boot &amp;gt; 2.2.6
&lt;/h2&gt;

&lt;p&gt;If your application uses JUnit 5, you can't use the &lt;code&gt;@ClassRule&lt;/code&gt; anymore. Fortunately, Testcontainers provides a solution to write tests with JUnit Jupiter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.testcontainers&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;junit-jupiter&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${testcontainers.version}&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this dependency and a more recent version of Spring Boot (&amp;gt; 2.2.6) the basic integration test looks like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// JUnit 5 example with Spring Boot &amp;gt;= 2.2.6&lt;/span&gt;
&lt;span class="nd"&gt;@Testcontainers&lt;/span&gt;
&lt;span class="nd"&gt;@SpringBootTest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;webEnvironment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WebEnvironment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RANDOM_PORT&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationIT&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

  &lt;span class="nd"&gt;@Container&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;PostgreSQLContainer&lt;/span&gt; &lt;span class="n"&gt;postgreSQLContainer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PostgreSQLContainer&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withPassword&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"inmemory"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withUsername&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"inmemory"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

  &lt;span class="nd"&gt;@DynamicPropertySource&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;postgresqlProperties&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DynamicPropertyRegistry&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"spring.datasource.url"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;postgreSQLContainer:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;getJdbcUrl&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"spring.datasource.password"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;postgreSQLContainer:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;getPassword&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"spring.datasource.username"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;postgreSQLContainer:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;getUsername&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="nd"&gt;@Test&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;contextLoads&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Integration test with JUnit 5 and Spring Boot &amp;lt; 2.2.6
&lt;/h2&gt;

&lt;p&gt;If your application makes use of JUnit 5 but is using a Spring Boot version &amp;lt; 2.2.6, you don't have access to the &lt;code&gt;@DynamicPropertySource&lt;/code&gt; feature. &lt;/p&gt;

&lt;p&gt;A possible integration test to verify a REST API endpoint is working as expected looks like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// JUnit 5 example with Spring Boot &amp;lt; 2.2.6&lt;/span&gt;
&lt;span class="nd"&gt;@Testcontainers&lt;/span&gt;
&lt;span class="nd"&gt;@SpringBootTest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;webEnvironment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WebEnvironment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RANDOM_PORT&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@ContextConfiguration&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initializers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DeletePersonIT&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Initializer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DeletePersonIT&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

  &lt;span class="nd"&gt;@Container&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;PostgreSQLContainer&lt;/span&gt; &lt;span class="n"&gt;postgreSQLContainer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PostgreSQLContainer&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withPassword&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"inmemory"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withUsername&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"inmemory"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

  &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;PersonRepository&lt;/span&gt; &lt;span class="n"&gt;personRepository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

  &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;TestRestTemplate&lt;/span&gt; &lt;span class="n"&gt;testRestTemplate&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Initializer&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ApplicationContextInitializer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ConfigurableApplicationContext&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ConfigurableApplicationContext&lt;/span&gt; &lt;span class="n"&gt;configurableApplicationContext&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="nc"&gt;TestPropertyValues&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TestPropertyValues&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"spring.datasource.url="&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;postgreSQLContainer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getJdbcUrl&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
        &lt;span class="s"&gt;"spring.datasource.password="&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;postgreSQLContainer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPassword&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
        &lt;span class="s"&gt;"spring.datasource.username="&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;postgreSQLContainer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUsername&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
      &lt;span class="o"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;applyTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configurableApplicationContext&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="nd"&gt;@Test&lt;/span&gt;
  &lt;span class="nd"&gt;@Sql&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/testdata/FILL_FOUR_PERSONS.sql"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;testDeletePerson&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;testRestTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/persons/1"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;personRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findAll&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;assertFalse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;personRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findAll&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Phil"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find more integration test examples for this demo Spring Boot CRUD API application using PostgreSQL on &lt;a href="https://github.com/rieckpil/blog-tutorials/tree/master/spring-boot-integration-tests-testcontainers"&gt;GitHub&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Further integration test-related tutorials for Spring Boot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://rieckpil.de/spring-boot-functional-tests-with-selenium-and-testcontainers/"&gt;Spring Boot Functional Tests with Selenium and Testcontainers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rieckpil.de/spring-boot-integration-tests-with-wiremock-and-junit-5/"&gt;Spring Boot Integration Tests with WireMock and JUnit 5&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy integration-testing with Spring Boot, Testcontainers and JUnit, &lt;br&gt;
Phil&lt;/p&gt;

</description>
      <category>java</category>
      <category>springboot</category>
      <category>junit</category>
      <category>testing</category>
    </item>
    <item>
      <title>Deploy a React application to Kubernetes in 5 easy steps</title>
      <dc:creator>Philip Riecks</dc:creator>
      <pubDate>Tue, 09 Jul 2019 09:55:50 +0000</pubDate>
      <link>https://forem.com/rieckpil/deploy-a-react-application-to-kubernetes-in-5-easy-steps-516j</link>
      <guid>https://forem.com/rieckpil/deploy-a-react-application-to-kubernetes-in-5-easy-steps-516j</guid>
      <description>&lt;p&gt;Kubernetes is currently the de-facto standard for deploying applications in the cloud. Every major cloud provider offers a dedicated Kubernetes service (eg. Google Cloud with GKE, AWS with EKS, etc.) to deploy applications in a Kubernetes cluster. &lt;/p&gt;

&lt;p&gt;There are many reasons for choosing Kubernetes for deploying your React application:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;unified and standardized deployment model across the cloud providers&lt;/li&gt;
&lt;li&gt;robustness against downtime as several containers are deployed (horizontal scaling)&lt;/li&gt;
&lt;li&gt;handling peak traffic with auto-scaling&lt;/li&gt;
&lt;li&gt;zero-downtime deployments, canary deployments, etc.&lt;/li&gt;
&lt;li&gt;simple A/B testing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this post, I'll demonstrate how to deploy a React application to a Kubernetes cluster with five easy steps. To follow the article you just need basic Docker knowledge. &lt;/p&gt;

&lt;p&gt;As we all want quick feedback while learning a new topic I'll use a local Kubernetes cluster for this example. You can spin up this local Kubernetes cluster in minutes if you have a Docker engine with Kubernetes support installed on your machine. This is available since version &lt;strong&gt;18.06.0&lt;/strong&gt; for both Docker Desktop for &lt;a href="https://docs.docker.com/docker-for-windows/kubernetes/" rel="noopener noreferrer"&gt;Windows&lt;/a&gt;/&lt;a href="https://docs.docker.com/docker-for-mac/kubernetes/" rel="noopener noreferrer"&gt;Mac&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's get started...&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create the React application
&lt;/h3&gt;

&lt;p&gt;I start off with a fresh React application created with &lt;code&gt;create-react-app&lt;/code&gt; from &lt;a href="https://github.com/facebook/create-react-app" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

➜ node &lt;span class="nt"&gt;-v&lt;/span&gt;
v10.16.0
➜ npm &lt;span class="nt"&gt;-v&lt;/span&gt;
6.9.0
➜ npx create-react-app react-app-kubernetes
npx: installed 91 &lt;span class="k"&gt;in &lt;/span&gt;5.787s

Creating a new React app &lt;span class="k"&gt;in&lt;/span&gt; /Users/rieckpil/Desktop/react-app-kubernetes.

Installing packages. This might take a couple of minutes.
Installing react, react-dom, and react-scripts...

...

Happy hacking!


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will create all the required configuration and files to start developing a new React application.&lt;/p&gt;

&lt;p&gt;Make sure you are able to start and access the React application on your local machine at &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; using &lt;code&gt;npm start&lt;/code&gt;. For the further sections, we need the optimized production build of the React application which is created with:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

➜ npm run-script build
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; react-app-kubernetes@0.1.0 build /Users/rieckpil/Desktop/junk/react-app-kubernetes
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; react-scripts build

Creating an optimized production build...
Compiled successfully.

File sizes after &lt;span class="nb"&gt;gzip&lt;/span&gt;:

  36.44 KB  build/static/js/2.b41502e9.chunk.js
  762 B     build/static/js/runtime~main.a8a9905a.js
  602 B     build/static/js/main.28647029.chunk.js
  517 B     build/static/css/main.2cce8147.chunk.css

...


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you have an existing React application you can skip this section and continue with the next one. Just make sure you have the production build of your application available at &lt;code&gt;build/&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Dockerize the React application
&lt;/h3&gt;

&lt;p&gt;For deploying the React application to Kubernetes, we need to package it inside a container. Kubernetes supports several container engines, but Docker is currently the most adopted one.&lt;/p&gt;

&lt;p&gt;To create a Docker container we need a &lt;code&gt;Dockerfile&lt;/code&gt; at the root level of our React application folder. This file defines what OS is used, how it is configured and what other applications are running inside this container.&lt;/p&gt;

&lt;p&gt;For this example, I'm using &lt;a href="https://www.nginx.com/" rel="noopener noreferrer"&gt;nginx&lt;/a&gt; to serve the content of the React application and the simplest &lt;code&gt;Dockerfile&lt;/code&gt; looks like the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; nginx:1.17&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; build/ /usr/share/nginx/html&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Our Docker container inherits everything from the official &lt;code&gt;nginx:1.17&lt;/code&gt; Docker image and just copies the React production build to the container.&lt;/p&gt;

&lt;p&gt;To speed up the creation of the Docker container, make sure you add a &lt;code&gt;.dockerignore&lt;/code&gt; to  your project to exclude the &lt;code&gt;node_modules&lt;/code&gt; from being sent to the Docker context:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

node_modules


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Once you have your React production build in place, start your Docker engine and execute the following command to create the Docker image:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

➜ docker build &lt;span class="nt"&gt;-t&lt;/span&gt; my-react-app &lt;span class="nb"&gt;.&lt;/span&gt;
Step 1/2 : FROM nginx:1.17
1.17: Pulling from library/nginx
fc7181108d40: Pull &lt;span class="nb"&gt;complete
&lt;/span&gt;d2e987ca2267: Pull &lt;span class="nb"&gt;complete
&lt;/span&gt;0b760b431b11: Pull &lt;span class="nb"&gt;complete
&lt;/span&gt;Digest: sha256:96fb261b66270b900ea5a2c17a26abbfabe95506e73c3a3c65869a6dbe83223a
Status: Downloaded newer image &lt;span class="k"&gt;for &lt;/span&gt;nginx:1.17
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; f68d6e55e065
Step 2/2 : COPY build/ /usr/share/nginx/html
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 35d48f28a918
Successfully built 35d48f28a918
Successfully tagged my-react-app:latest


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For a more production-ready Docker container, make sure you customize the nginx configuration for your needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Connect to your Kubernetes cluster
&lt;/h3&gt;

&lt;p&gt;To start your local Kubernetes cluster, open the Docker Desktop preferences and switch to the Kubernetes tab:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw95mdfaziozvv09try35.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw95mdfaziozvv09try35.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here you can enable the Kubernetes support. If you enable it for the first time, the cluster creation &lt;strong&gt;may take some time&lt;/strong&gt;, as several Docker images are downloaded in the background.&lt;/p&gt;

&lt;p&gt;Once your local Kubernetes cluster is running, connect to it via &lt;code&gt;kubectl&lt;/code&gt; (if missing, download &lt;a href="https://kubernetes.io/docs/tasks/tools/install-kubectl/" rel="noopener noreferrer"&gt;here&lt;/a&gt;):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

➜ kubectl config use-context docker-for-desktop
Switched to context &lt;span class="s2"&gt;"docker-for-desktop"&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
➜ kubectl get nodes
NAME                 STATUS    ROLES     AGE       VERSION
docker-for-desktop   Ready     master    3d        v1.10.11
➜ kubectl cluster-info
Kubernetes master is running at https://localhost:6443
KubeDNS is running at https://localhost:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use &lt;span class="s1"&gt;'kubectl cluster-info dump'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;With this Kubernetes support of Docker Desktop you'll get a local single node &lt;em&gt;cluster&lt;/em&gt; which is nice for evaluation and learning purposes. &lt;/p&gt;

&lt;p&gt;If you want to deploy your React application to a Kubernetes cluster in the cloud, start here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Google Kubernetes Engine (GKE) &lt;a href="https://cloud.google.com/kubernetes-engine/docs/quickstart" rel="noopener noreferrer"&gt;quickstart&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Azure Kubernetes Engine (AKS) &lt;a href="https://azure.microsoft.com/de-de/services/kubernetes-service/" rel="noopener noreferrer"&gt;quickstart&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;AWS Kubernetes Engine (EKS) &lt;a href="https://aws.amazon.com/de/eks/" rel="noopener noreferrer"&gt;quickstart&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Oracle Container Engine for Kubernetes (OKE) &lt;a href="https://docs.cloud.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengcreatingclusterusingoke.htm" rel="noopener noreferrer"&gt;quickstart&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 4: Upload the Docker image to your container registry
&lt;/h3&gt;

&lt;p&gt;To be able to pull the Docker image within the Kubernetes cluster, we need to upload the image to a &lt;strong&gt;Docker registry&lt;/strong&gt;. For a cloud deployment, you usually get a dedicated registry from the cloud provider to upload your image. As we are deploying the application to a local cluster, we need a local Docker registry.&lt;/p&gt;

&lt;p&gt;You can create a local Docker registry with the following statement:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

➜ docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 5000:5000 &lt;span class="nt"&gt;--restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;always &lt;span class="nt"&gt;--name&lt;/span&gt; registry registry:2


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For uploading our previously created React Docker image, we have to &lt;em&gt;tag&lt;/em&gt; the image with the hostname and port of the registry:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

➜ docker tag my-react-app localhost:5000/my-react-app


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And can now finally push the image to our Docker registry:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

➜ docker push localhost:5000/my-react-app
The push refers to repository &lt;span class="o"&gt;[&lt;/span&gt;localhost:5000/my-react-app]
9d9745936581: Pushed
d2f0b6dea592: Pushed
197c666de9dd: Pushed
cf5b3c6798f7: Pushed
latest: digest: sha256:66c94bdba6b06d1964a764cd14dc97d8adf202b02ab6e6fbd10b23ad4a8554a1 size: 1158


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now we are ready to deploy the application to the Kubernetes cluster.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Deploy the React application
&lt;/h3&gt;

&lt;p&gt;Usually, every Kubernetes object (&lt;code&gt;service&lt;/code&gt;, &lt;code&gt;pod&lt;/code&gt;, &lt;code&gt;deployment&lt;/code&gt;, etc.) is described in a &lt;code&gt;.yaml&lt;/code&gt; file but &lt;code&gt;.json&lt;/code&gt; is also possible. &lt;/p&gt;

&lt;p&gt;For deploying the React application to Kubernetes we need a so-called &lt;code&gt;deployment&lt;/code&gt;. This Kubernetes entity makes sure our application will have as many replicas (parallel pods) as we define. In addition, we can define the Docker image we want to use, what ports are used and further metadata for our application:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-react-app&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-react-app&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-react-app&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-react-app&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localhost:5000/my-react-app&lt;/span&gt;
          &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Always&lt;/span&gt;
          &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
      &lt;span class="na"&gt;restartPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Always&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;With just the &lt;code&gt;deployment&lt;/code&gt; we wouldn't be able to access our application from outside. To expose applications, Kubernetes offers a so-called &lt;code&gt;service&lt;/code&gt;. Using a &lt;code&gt;service&lt;/code&gt; we can define which ports to expose to the cluster/outside. &lt;/p&gt;

&lt;p&gt;There are several different types of a Kuberntes &lt;code&gt;service&lt;/code&gt; and I'm choosing the simplest one for your example: &lt;code&gt;NodePort&lt;/code&gt;. This type will expose a defined port on every node in our Kubernetes cluster (with the local Kubernetes cluster, we just have one node) and map it to an application's port:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-react-app&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NodePort&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
      &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
      &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
      &lt;span class="na"&gt;nodePort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;31000&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-react-app&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Both the &lt;code&gt;service&lt;/code&gt; and &lt;code&gt;deployment&lt;/code&gt; can be added to a single &lt;code&gt;.yaml&lt;/code&gt; file with a &lt;code&gt;---&lt;/code&gt; for separation. In our example, I've created one &lt;code&gt;deployment.yaml&lt;/code&gt; file to store both:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="c1"&gt;# more ...&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="c1"&gt;# more ...&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You can now use this file to deploy your application to Kubernetes with:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

➜ kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; deployment.yaml
deployment.apps &lt;span class="s2"&gt;"my-react-app"&lt;/span&gt; created
service &lt;span class="s2"&gt;"my-react-app"&lt;/span&gt; created


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And can check that everything is running using:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

➜ kubectl get pods
NAME                            READY     STATUS    RESTARTS   AGE
my-react-app-6c8b5c4759-fgb2q   1/1       Running   0          7m
my-react-app-6c8b5c4759-jrzzj   1/1       Running   0          7m
➜ kubectl get deployment
NAME           DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
my-react-app   2         2         2            2           7m
➜ kubectl get service
NAME           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT&lt;span class="o"&gt;(&lt;/span&gt;S&lt;span class="o"&gt;)&lt;/span&gt;        AGE
kubernetes     ClusterIP   10.96.0.1       &amp;lt;none&amp;gt;        443/TCP        3d
my-react-app   NodePort    10.99.224.141   &amp;lt;none&amp;gt;        80:31000/TCP   7m


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Once everything is up and running, visit &lt;a href="http://localhost:31000" rel="noopener noreferrer"&gt;http://localhost:31000&lt;/a&gt; on your machine and you should see your React application, now served from a Kubernetes cluster:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb1ud6vbzbn5pec9c3ult.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb1ud6vbzbn5pec9c3ult.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To remove the React application from your Kubernetes cluster, just run &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

➜ kubectl delete service,deployment my-react-app
service &lt;span class="s2"&gt;"my-react-app"&lt;/span&gt; deleted
deployment.extensions &lt;span class="s2"&gt;"my-react-app"&lt;/span&gt; deleted


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You can find the whole codebase for this example on &lt;a href="https://github.com/rieckpil/blog-tutorials/tree/master/react-app-kubernetes" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For further React related posts, visit my &lt;a href="https://rieckpil.de" rel="noopener noreferrer"&gt;blog&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://rieckpil.de/howto-pdf-preview-with-react/" rel="noopener noreferrer"&gt;Preview PDF files with React
&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rieckpil.de/howto-microprofile-jwt-authentication-with-keycloak-and-react/" rel="noopener noreferrer"&gt;MicroProfile JWT Authentication with Keycloak and React
&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rieckpil.de/howto-up-and-download-files-with-react-and-spring-boot/" rel="noopener noreferrer"&gt;Up- and download files with React and Spring Boot&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Have fun deploying your React applications to Kubernetes,&lt;br&gt;
Phil&lt;/p&gt;

</description>
      <category>react</category>
      <category>kubernetes</category>
      <category>docker</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
