Unit Tests, Mock scenarios and Integration tests
Following our previous chapter, let's discuss how to properly test more complex applications.
A key aspect of enterprise code is its testability and spring offers great support for this. Tests covering great chunks of layers in the application can be called integration tests.
All goodies seen in previous chapter about testing are also available, along with cool new ones.
Spring tests basics
Whenever testing spring applications, the IoC container should be available for a more realistic test scenario. It can be achieved by simply adding the @SpringBootTest annotation in the test class:
@SpringBootTest
class Project015ApplicationTests {
@Test
fun contextLoads() {
}
}That way, all test cases will have a complete spring context at disposal to its scenarios.
MockBean
Mock beans injected by spring are handy, sometimes the test boundaries are restrained but the IoC container is till needed:
import org.mockito.Mockito.`when` as _when
// ...
@SpringBootTest
class ProductServiceTest {
@MockitoBean
private lateinit var productRepository: ProductRepository
@Autowired
private lateinit var productService: ProductService
@BeforeEach
fun setup() {
_when(productRepository.findAll())
.thenReturn(listOf(Product(33L, "Shoes")))
}
@Test
fun shouldListProducts() {
val result = productService.list()
Assert.assertEquals(33L, result[0].id)
}
}MockMvc
When dealing with spring controllers, the ideal way to perform the test is by simulating the request. MockMvc Helps on that matter:
// ...
@SpringBootTest
@AutoConfigureMockMvc
class ProductControllerTest {
@Autowired
private lateinit var mockMvc: MockMvc
@Test
fun shouldListProducts() {
val result = mockMvc.perform(
MockMvcRequestBuilders.get("/products")
).andReturn().response
Assertions.assertEquals(200, result.status)
}
// ...
}TestContainers
Finally, TestContainers can help by extending the test boundaries to the database.
If the project uses liquibase or depends on a certain kind of state in the db, then the container configuration grants this to test scenarios.
The most transparent way to use them is by provisioning a TestConfiguration:
//...
@TestConfiguration(proxyBeanMethods = false)
class TestcontainersConfiguration {
@Bean
@ServiceConnection
fun postgresContainer(): PostgreSQLContainer<*> = PostgreSQLContainer(
DockerImageName.parse("postgres:16-alpine")
).withEnv(
mapOf(
"POSTGRES_DB" to "products",
"POSTGRES_USER" to "enterprise",
"POSTGRES_PASSWORD" to "enterprise"
)
)
}Then you import it on testcases needing it:
// ...
@SpringBootTest
@Import(TestcontainersConfiguration::class)
class Project015ApplicationTests {
@Test
fun contextLoads() {
}
}Check the sample project to see it in action and then head to the next chapter