Reputation: 11
I'm working in a Kotlin application that uses Spring Boot, and one of the Controller classes uses Spring Session with JDBC to persist session IDs:
@RestController
@RequestMapping("/health")
class HealthController {
// healthCheck returns true iff the application is healthy.
@GetMapping
fun healthCheck(session: HttpSession): Map<String, Any> {
val resp = LinkedHashMap<String, Any>()
resp["status"] = true
resp["sessionId"] = session.id
println("health check successful")
return resp
}
}
Every time the health
endpoint is called, it saves a session in the underlying application DB that is configured with JDBC if the session does not exist, which is expected behavior:
spring:
datasource:
url: jdbc:postgresql://${DB_HOST:localhost}:5432/${DB_NAME:}
username: ${DB_USERNAME:postgres}
password: ${DB_PASSWORD:password}
session:
store-type: jdbc
However, when I run my unit test, that saves the session data in my underlying DB as well:
@SpringBootTest
@AutoConfigureMockMvc
class HealthControllerTest @Autowired constructor(private val mockMvc: MockMvc) {
// TODO(shubham): Modify unit test to use mock DB under the hood instead of configured app DB
@Test
fun `healthCheck should return status true`() {
mockMvc.perform(get("/health"))
.andExpect(status().isOk)
.andExpect(jsonPath("$.status").value(true))
}
}
How can I modify my unit test to use a mock DB under the hood instead of the configured application DB?
I have tried mocking the JDBCOperations
as well as the JDBCTemplate
classes hoping that they would be used by Spring Session, but those get ignored in my unit test and don't get mocked (i.e. the underlying DB is still being used).
Examples:
NamedParameterJdbcTemplate
with the @Mock
annotation:@SpringBootTest
@AutoConfigureMockMvc
class HealthControllerTest @Autowired constructor(private val mockMvc: MockMvc) {
@Mock
private lateinit var template: NamedParameterJdbcTemplate
// TODO(shubham): Modify unit test to use mock DB under the hood instead of configured app DB
@Test
fun `healthCheck should return status true`() {
// mock behavior of template
Assertions.assertNotNull(template)
`when`(template.update(any(String::class.java), any(MapSqlParameterSource::class.java)))
.thenAnswer {
1
}
mockMvc.perform(get("/health"))
.andExpect(status().isOk)
.andExpect(jsonPath("$.status").value(true))
// assert that the mock was called with the specified args
verify(template).update(any(String::class.java), any(MapSqlParameterSource::class.java))
}
}
Error:
Wanted but not invoked:
template.update(
<any java.lang.String>,
<any org.springframework.jdbc.core.namedparam.MapSqlParameterSource>
);
-> at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.update(NamedParameterJdbcTemplate.java:337)
Actually, there were zero interactions with this mock.
NamedParameterJdbcTemplate
with the @MockBean
annotation:...
@MockBean
private lateinit var template: NamedParameterJdbcTemplate
...
Error:
Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'userRepository' defined in com.app.server.repository.UserRepository defined in @EnableJdbcRepositories declared on JdbcRepositoriesRegistrar.EnableJdbcRepositoriesConfiguration: Cannot resolve reference to bean 'org.springframework.data.jdbc.core.mapping.JdbcMappingContext' while setting bean property 'mappingContext'
JDBCTemplate
with the @Mock
annotation:...
@Mock
private lateinit var template: JDBCTemplate
Error:
Wanted but not invoked:
template.update(
<any java.lang.String>,
<any org.springframework.jdbc.core.namedparam.MapSqlParameterSource>
);
-> at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:1024)
Actually, there were zero interactions with this mock.
JDBCTemplate
with the @MockBean
annotation:...
@MockBean
private lateinit var template: JDBCTemplate
...
Error:
Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'userRepository' defined in com.app.server.repository.UserRepository defined in @EnableJdbcRepositories declared on JdbcRepositoriesRegistrar.EnableJdbcRepositoriesConfiguration: Cannot resolve reference to bean 'org.springframework.data.jdbc.core.mapping.JdbcMappingContext' while setting bean property 'mappingContext'
JdbcOperations
with the @Mock
annotation:...
@Mock
private lateinit var jdbcOperations: JdbcOperations
Error:
Wanted but not invoked:
jdbcOperations.update(
<any java.lang.String>,
<any org.springframework.jdbc.core.namedparam.MapSqlParameterSource>
);
-> at com.ordrsmart.server.controller.HealthControllerTest.healthCheck should return status true(HealthControllerTest.kt:63)
Actually, there were zero interactions with this mock.
JdbcOperations
with the @MockBean
annotation:...
@MockBean
private lateinit var jdbcOperations: JdbcOperations
...
Error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of method namedParameterJdbcTemplate in org.springframework.boot.autoconfigure.jdbc.NamedParameterJdbcTemplateConfiguration required a bean of type 'org.springframework.jdbc.core.JdbcTemplate' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.jdbc.core.JdbcTemplate' in your configuration.
Upvotes: 1
Views: 210