Thursday, May 21, 2020

Integration Testing and TestContainers

I must be behind the times. TestContainers is a library ported to many popular languages and frameworks that assists in starting up Docker containers during your test runtime. It initially was pushed to Github in 2015 and I am only NOW discovering it. Oh the troubles and headaches this would have saved me.

For years and years, I have struggled to find the correct approach to writing code and building tests in the correct balance. It was always one extreme or the other. I would write enormous amounts of code without any unit tests. I mean, come on. The project is due very soon. Who has time to write tests? Then you get the other extreme: Test-driven development. It is like being in school all over again where you show your work in Math class. How can we forget my favorite situation: 50% of your unit tests do not consistently pass. These are just mounting headaches and problems.

How do we act like perfect programming angels where you check off ALL the boxes:
  • Continuous integration where all tests run
  • Tests that pass every time
  • Integration tests that ensure connectivity to other component
  • Tests that do not depend on external databases being in the perfect state
  • 367% code coverage (you know they would ask for it if they could)
  • Shift Left test concepts
    • Involving QA earlier
    • Getting developers to assist in the test phase
  • Delivering the code at a fast pace with adequate tests to prove your work
  • Make the bosses happy
TestContainers is the metaphorical programming angel sent from binary heaven. When used properly, TestContainers allows you to build integration tests using Docker in your test runtime. Let's get setup!

Setup your environment

Make sure you have Docker installed on your development machine: Docker Desktop 

Import TestContainers in your language of choice. Check HERE for support of your language. Other languages supported are Java, Python, Golang, Node, etc. Here is the link to the Scala repo: TestContainers-Scala. For my specific use case, I'll be using ScalaTest so I'll be importing the module to run TestContainers from ScalaTest.

Build your First Test

Now, let's create our first test! I am going to use Redis because Redis solves all of the world's problems. FACT!



As you can see from our test, it simply starts the Redis Docker container, calls PING, and asserts that PONG is replied back from the Redis process running in the Docker container.

Build a Test Harness

Now, setting up your test fixtures from here forward will be a major contributor to the success of this pattern. Remember our goals: shift left, deliver on time, pass tests, coverage, etc. Instead of compiling your solution, deploying to your Dev/QA environment, and telling QA to find the issues; bring the testing effort back to your local machine. Think of the applicable use cases and states of your service and put them into the test fixtures using TestContainers. I like to call it "creating a test harness". Create an interface that is easily implemented to serve as your way to create strong integration tests for your service.


Now we have create a concise interface on how to perform integration tests on this service. There is a clear pattern: purgeAllData to empty the database if necessary, import flat files or entries, call getDataId to retrieve data, and assert on the response.

Make a Test Harness Implementation

In the example posted above we have used the test harness to assert every possible behavior of the API/service abstraction. At this point, I have 99% confidence that everything I built will work perfectly when I move this to the QA and PRODUCTION environments. At worst, I will have some configuration issues. These tests will carry on with the service for the life of the codebase. Furthermore, the usage of the test harness is SO basic that developers of ALL levels of experience should be able to expand your test cases with little guidance.

You have now finished a POC of using TestContainers. In doing so, you have shifted left, built a solid and reliable test base that passes every time, and will have incredible levels of confidence that your service will operate appropriately when moved through QA and Production environments. I recently used this paradigm on a mission critical project at work. Not only were we ahead of schedule every single time, but we have YET to receive a single solitary bug report. Furthermore, we have received very minimal feedback from QA for any major changes. The bosses are happy because we pushed to PROD early and have an enormous test base that runs against multiple integrated components. We have absolute confidence in every build we push out.

Pull my Github repo hosting this blog post HERE and see for yourself. Join TestContainers with no regrets. Watch the reliability of your service soar.

No comments:

Post a Comment