This article is the third in a series about system testing:
- Dockerized testing vs end-to-end testing
- How to setup Dockerized testing
- Readable Java system tests with good old JUnit
JUnit is poorly named. Given the name, people tend to think that it should only be used to write Java unit tests. And then people feel a bit hesitant about writing their integration tests with JUnit too. When they start with system tests, they often think they need another driver for their tests. Sure, maybe using another abstraction layer and a custom domain specific language (BDD), you can make the tests more readable for a non-programmer. That often comes at the cost of making the tests less readable for the programmers. And if we are honest, who’s going to read the tests the most? Perhaps just naming test classes and methods well and writing readable code can suffice?
The point I am trying to make is that sometimes, you can just do the simplest thing and write your system tests using plain Java and JUnit. Fewer languages, libraries and frameworks can reduce the mental load of everyone in the team.
I’ve created a java system test example project which demonstrates how you can:
- Write system tests using Java, RestAssured and JUnit. Everyone who codes in Java knows JUnit and RestAssured gives you a nice given/when/then structure. Of course, when you are testing something other than a REST service, another test tool might be needed.
- Group the tests into suites. Maybe you need different test suites for different environments. Maybe you even want to run some non-intrusive tests on production after each deployment? (But “Testing in Production” sounds like a topic for another blog post.)
- Package the test classes in a single fat jar file, so it can be published to a repository and downloaded and run when needed. Maybe you want to run the tests in a pipeline, and you don’t check out your repository multiple times in your pipeline, do you? 😉 If you disagree, google “build once, deploy many” and see if that changes your mind.
- Run the system test jar with JUnitCore runner, so you don’t need to write a custom Main class or anything silly like that.
The code can be found at: https://github.com/betrcode/java-system-test-example
Let’s try it!
Clone the repository: https://github.com/betrcode/java-system-test-example
We need to have something to run the tests against. Start a dockerized test environment by running:
docker-compose -f docker-compose/docker-compose.yml up -d
It will download all docker images and start your entire system, ready to be tested. In my previous post I wrote about how to setup dockerized testing.
$ docker-compose -f docker-compose/docker-compose.yml up -d Creating network "dockercompose_default" with the default driver Creating myapp-database Creating myapp-rabbit Creating external-nasty-xml-stub Creating myapp
Run the system test against the dockerized environment:
$ ./gradlew clean test -DconfigFile=config/dockerized-config.yml > Task :test se.bettercode.systemtest.suite.FirstTestSuite > se.bettercode.systemtest.ExampleTest.restEndpointIsOK PASSED BUILD SUCCESSFUL in 4s 4 actionable tasks: 4 executed
This is how I would run the system tests when on my local developer machine. In a Continuous Delivery Pipeline, I would download a previously built jar and execute the tests by running the jar.
Let’s build a jar with the tests and execute the jar:
$ ./gradlew clean jar BUILD SUCCESSFUL in 2s 3 actionable tasks: 3 executed $ java -DconfigFile="config/dockerized-config.yml" -cp build/libs/system-tests-0.1.1-SNAPSHOT.jar org.junit.runner.JUnitCore se.bettercode.systemtest.suite.FirstTestSuite JUnit version 4.12 [main] INFO se.bettercode.systemtest.testutils.TestSetup - Config loaded .[main] INFO se.bettercode.systemtest.ExampleTest - Testing: http://localhost:8080/get Time: 1,326 OK (1 test)
Finally, you can take your dockerized test environment down:
$ docker-compose -f docker-compose/docker-compose.yml down Stopping myapp ... done Stopping myapp-database ... done Stopping external-nasty-xml-stub ... done Stopping myapp-rabbit ... done Removing myapp ... done Removing myapp-database ... done Removing external-nasty-xml-stub ... done Removing myapp-rabbit ... done Removing network dockercompose_default
You can run this on your local machine, or on a CI server. You can run it on any machine that has Java and Docker. If you use branches, you can run it on every branch. Running system tests early is better than when the code has already been merged to master and deployed to some shared staging environment!
If you want to do something similar you can use this example code as a starting point and expand it. I hope this was valuable to you, please feel free to comment below.
If you are interested in dockerizing your test setup, read my previous post “How to setup Dockerized testing”.
Hi,
The entire series was of great help, thanks.
We are trying to do a system test of a service startup, in this case it is seeming to be a bit tricky where we want to create a docker environment which contains all the artifacts to run our code, and then the actual test is the “java -cp xyz.jar:abc.jar” command.
And the assertion is going to be a 200 on a GET to the service entrypoint.
Regards,
G