When developing an application, you need to ensure that it works as expected before releasing it. One of the most convenient ways to do it is to write tests and run them automatically after making changes in the source code. However, writing tests is not always an easy-to-do task. Moreover, it becomes even more complex when you develop a framework-managed application because a framework imposes some restrictions on the architecture of your application and requires using some framework-specific concepts.
This topic introduces the basics of testing Spring applications to help you understand the technology-related topics that will follow.
The ideas behind testing Spring applications
A typical Spring application consists of several layers implemented with the help of container-managed objects called beans and the dependency injection mechanism. Most likely, the application will include an API layer, the core logic layer, and a database access layer. Spring Boot allows testing all these layers using unit or integration tests. Here, unit testing is used when you need to test one bean (or component) in isolation from others and integration testing is used when you need to test several beans together in accordance with their dependencies.
The key difference between testing pure Java or Kotlin and Spring-based applications is that when testing a Spring app, you need to deal with container-managed objects and test them. This means that your test should either start the container and run tests against it, or you have to mock the container and emulate it in some other way.
Although you need to test beans that form the backbone of any Spring application, you still shouldn't forget about standard Java or Kotlin classes like utilities. They also have to be tested like in pure Java or Kotlin applications.
Considering that integration tests may require a lot of time to start and execute due to their heavy reliance on Spring context, dependency injection, and possibly database access, the best practice is to keep them separate from unit tests, which are usually lightweight and fast. For example, if you use Maven, you can create a separate module for integration tests and run them only periodically so that it doesn't take too much every time you rebuild the app locally.
It is also worth mentioning that modern Spring applications usually use Maven or Gradle as the build tool and all the written tests are integrated into the lifecycle of the tool. At the same time, it's also true for modern pure Java or Kotlin applications.
The libraries for testing with Spring Boot
The Java ecosystem, which Kotlin is part of, includes a lot of amazing libraries for both unit and integration tests. One of such libraries is JUnit, which is the de-facto standard for testing most modern Java or Kotlin applications. This library provides a comprehensive foundation for developer-side testing on JVM using a lot of facilities to manage the tests. The good news is that JUnit can also be used for Spring-based applications. However, to make the library work with container-managed objects and classes, especially when writing integration tests, the programmer needs to use some specific tools that allow the tests to start the Spring container that creates beans and injects their dependencies. All the required tools are included in the Spring Testing Framework, which is published as spring-test and can be added to your project as a dependency (for example, using Maven or Gradle).
Spring Boot provides a convenient starter called spring-boot-starter-test that brings spring-test together with JUnit and a bunch of other commonly-used libraries:
Mockito is a framework for mocking Java or Kotlin objects. It simplifies the development of unit tests for objects and classes with external dependencies by replacing the dependencies with imitating objects.
AssertJ enables writing fluent assertions in tests using a rich human-readable API.
Hamcrest assists with writing tests by adding declarative matchers.
This is not a complete list of the libraries provided by this starter. There are also some useful libraries for processing JSON and XML formats, as well as other tools.
Note that you are not obliged to use all these libraries for every single test. It is possible to use only JUnit together with the Spring Testing Framework. However, other libraries can significantly reduce the complexity of writing tests.
At the same time, you don't have to use only the libraries included in the starter. You can easily integrate TestNG or something else as a good alternative.
Conclusion
In this topic, we pointed out the key difference between testing pure and Spring-based Java or Kotlin applications based on the need to use a container and a dependency injection mechanism. We also mentioned that integration tests are usually much more time-consuming than unit tests so it's worth keeping them separate from each other. We covered the most useful frameworks and libraries commonly used for testing Spring Boot applications such as JUnit, Spring Testing Framework, Mockito, AssertJ, Hamcrest, and others. All of them are included in spring-boot-starter-test, which can be added as a dependency to your Spring Boot application. In further topics, you will take a closer look at how to use the libraries listed in this topic for testing Spring-based applications.