Five Java frameworks for improving your automated testing
Improve your app development with these valuable testing frameworks and tools
Writing automated tests is a critical step in software development. A quality, automated testing suite provides rapid feedback to make sure a change meets requirements and also provides a valuable safety net so that a regression isn’t accidentally introduced. Automated testing also enables devops practices like Continuous Integration and Continuous Delivery, greatly increasing the speed and ease with which changes can be pushed to production.
In this article we will look at five frameworks that help improve the process of automated testing. These frameworks can help with writing tests for complicated use cases, improve readability, and improve reliability.
Automated tests serves as a form of “living documentation” for the system under test. Like any documentation, it is only useful if it accurately communicates the key concepts to the reader. AssertJ helps improve the readability of automated tests by creating a fluent assertion syntax. In AssertJ all assertions start with assertThat(actual). Which replaces for example, the sometimes confusing JUnit assertEquals(expected, actual) with assertThat(actual).isEqualTo(expected).
AssertJ is fully compatible with all versions of TestNG and JUnit, so you can start using AssertJ in your next tests without having to worry about a complex migration process.
JUnit5 was released nearly three years ago and now is as good as time as any to switch to the newest version of the popular testing framework! JUnit 5 introduces many improvements to the JUnit framework including:
- Java 8 support (JUnit 5 requires at least Java 8)
- Improved extensibility
- New Extensions API
- Improved parallel testing support
- Ordered testing support
- Backwards compatibility to JUnit 3.8
- and much more
I have covered JUnit 5 extensively in the past, and have a github repo available with examples on how to use JUnit 5’s new features which can be found here: https://github.com/wkorando/welcome-to-junit5-v2
When writing an automated test for a class or feature, often that class or feature depends upon another abstraction layer. Setting up an abstraction layer to be tested can be a complex and possibly fragile process, in such cases creating a mock would often be the better option. When you need to write a mock, Mockito is an excellent choice.
Mockito, like AssertJ, has an expressive syntax that not only makes creating a mock easy but also communicates its purpose so developers in the future, rather yourself or someone else, can understand the purpose of a test should it fail. Mockito uses the Given, When, Then, syntax popularized in Behavior Driven Development, for creating a mock, defining its behavior, and if needed, evaluating its state at the end of a test.
Spring Cloud Contract
Whether a microservice or a monolith, applications often need to communicate with external APIs. Writing automated tests that depend upon a remote service can be flaky. A test run could fail because the remote service was unavailable, data was missing, or a number of other reasons that don’t relate to the goal of the test itself.
To address this issue we have Spring Cloud Contract. Spring Cloud Contract supports a concept called contract testing which allows an API to be defined programmatically (e.g. using Groovy, YAML, Kotlin, et al.), and to then use that contract to test producers and consumers to verify they are fulfilling the behavior defined in the contract.
To see a working example on how to use Spring Cloud Contract, checkout my github repo: https://github.com/wkorando/collaborative-contract-driven-development-2-0
Spring Cloud Contract can be great for testing remote services, however it can’t cover every use case. Applications frequently depend upon databases, caches, and other services that don’t use HTTP or Message APIs. When you need to write automated integration tests for these services, TestContainers is an excellent option.
TestContainers is an extension to JUnit that allows a Docker container to be created and torn down within the life cycle of either a test case or if needed, a test class. With Docker’s flexibility of being able to run pretty much anything within a container, TestContainers allows for automated integration tests to be written that closely model what happens in production and for those tests to be portable and reliable.
Writing automated tests is a critical step in the software development life cycle. If you have been having trouble with writing automated tests or just want to improve the quality of your automated tests, then trying out these frameworks is an excellent place to start! The end-result of faster delivery of production-ready and production-tested applications benefits everyone involved.