Spring test #Transactional with #Nested junit classes - spring

I am encountering a problem in spring transactions: even though I see in the output that a transaction is created and rolled back after each test method; when I check the database content, it still keeps the document that was saved during the transaction.
I ran mongodb instance in replica mode following instructions from here https://www.mongodb.com/docs/manual/tutorial/deploy-replica-set-for-testing/.
My test class is defined as follows:
#Transactional
#AutoConfigureMockMvc
#ActiveProfiles(profiles = ["test"])
#DisplayName("Login Functionality Test")
#SpringBootTest(classes = [TestConfiguration::class])
class LoginFunctionalityTest #Autowired constructor(
private val mockMvc: MockMvc,
private val userRepository: UserRepository,
private val openUserRepository: OpenUserRepository,
private val patientRepository: PatientRepository,
private val passwordEncoder: PasswordEncoder,
private val jwtMatcher: JWTMatcher,
#Value("\${secret:sD1fRUWtBdfA8BNcbf}") private val fastLoginSecret: String
) {
private val existingUsersPassword = "SOME_PASSWORD"
private val userFastLoginTokenSecret = "ANY_SECRET"
private lateinit var existingUser: User
private lateinit var existingOpenUser: OpenUser
private lateinit var existingPatient: Patient
#BeforeEach
fun prepareContext() {
println("Init db")
existingUser = userRepository.save(User(null, now(), "test", "test", "test#test.test", passwordEncoder.encode(existingUsersPassword), UserAuthority.Patient, true, true))
existingOpenUser = openUserRepository.save(OpenUser(null, "ANY", nextExpiryDate()))
existingPatient = patientRepository.save(Patient(null, existingUser))
}
#DisplayName("Login Controller")
#Nested
inner class LoginControllerTest{
#Test
fun `should return unauthorized 401 status code when user logins with not existing email`() {
val invalidEmail = "INVALID_EMAIL"
assertThrows<EmptyResultDataAccessException> { userRepository.findByEmail(invalidEmail) }
mockMvc.post("/auth/login") {
param("username", invalidEmail)
param("password", existingUsersPassword)
}.andExpect {
jsonPath("$.status", equalToIgnoringCase(HttpStatus.UNAUTHORIZED.name))
jsonPath("$.error",StringContains(true,"Invalid login credentials"))
jsonPath("$.data", nullValue())
status { isUnauthorized() }
}
}
...
}
#ActiveProfiles(profiles = ["open-user"])
#DisplayName("Open User Login Controller")
#Nested
inner class OpenUserControllerLoginTest(){
#Test
fun `should return bad request 400 status when any of the required login parameters are missing`(){
val validFastLoginToken = String(Base64.getEncoder().encode(passwordEncoder.encode("$userFastLoginTokenSecret${existingUser.passwordHash}$fastLoginSecret").toByteArray()))
mockMvc
.post("/auth/login/fast") {
param("userId", existingUser.id!!)
param("secret", userFastLoginTokenSecret)
param("fastLoginToken", validFastLoginToken)
}.andExpect {
jsonPath("$.data.isUserActivated", equalTo(existingUser.activated))
jsonPath("$.data.accessToken",jwtMatcher.matches(existingUser))
jsonPath("$.status", equalToIgnoringCase(HttpStatus.OK.name))
jsonPath("$.error", nullValue())
status { isOk() }
content { contentType(MediaType.APPLICATION_JSON) }
}
}
...
}
The output in console has the "Init db" printed between creation and roll back of transaction.
It works correctly only if I move #ActiveProfiles from #Nested class to the outer class. Seems like #ActiveProfiles causes context reload but I don't know why this causes problems with transactions.

Related

Any benefit of adding a read Query in a service class in Spring

There is a code where there are single read query in a service class , does this have any effect in terms of performance/internal way hibernate runs the query ? As it's easier to just write the query in a facade/handler layer than a call the service class in read only ops
Run with hibernate here and PersonRepo is the envers class for the Person class annotated with #Entity
For eg)
#Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED, rollbackFor = [Exception::class])
#Service
class PersonService(
private val personRepo: PersonRepo
) {
fun getPersonById(personId: Long): PersonEntity {
return this.personRepo.findById(personId)?: throw ValidationException("person $personId does not exist")
}
}
when writing this query in handler/non service layer when should we
call it from a service class
#Component
class PersonHandler (
private val personRepo: PersonRepo,
private val personService: personService,
private val someOtherHandler: SomeOtherHandler
) {
fun displayPersonAge(personId: Long) {
val person = this.personService.getPersonById(person)
someOtherHandler(person)
}
}
get the object directly
#Component
class PersonHandler (
private val personRepo: PersonRepo,
private val personService: personService,
private val someOtherHandler: SomeOtherHandler
) {
fun displayPersonAge(personId: Long) {
val person = this.personRepo.findById(personId)?: throw ValidationException("person $personId does not exist")
someOtherHandler(person)
}
}
since both are one line here in kotlin , does adding it to a serviceClass have any benefit to it ? if it is run in a transaction/not in run in a transaction are there any effects on other db transactions and query performance under load

How to mock repository so it doesn't instert or pull data from DB

So my issue is that in my SpringBoot REST application im testing my RestController. The problem is that i don't know how to mock the repository so it doesn't get or puts data into the DB. I'm using Kotlin and Mockk for mocking
Here is my Repository
#Repository
interface StandingOrderRepository: CrudRepository<StandingOrder, Int> {
fun findByNameAndVariableSymbol(name: String, variableSymbol: String): List<StandingOrder>
fun findByValidFromBetween(fromDate: String, toDate: String): List<StandingOrder>
fun findByValidFromAfter(fromDate: String) : List<StandingOrder>
}
And here is my Test
#SpringBootTest
#AutoConfigureMockMvc
internal class StandingOrderResourceTest {
#Autowired
lateinit var mockMvc: MockMvc
#Autowired
lateinit var objectMapper: ObjectMapper
private val standingOrderMapper = mockk<StandingOrderMapper>()
private val standingOrderRepository = mockk<StandingOrderRepository>()
private val standingOrderServiceImpl = mockk<StandingOrderServiceImpl>()
private val standingOrderResource = StandingOrderResource(standingOrderServiceImpl)
val baseUrl = "/api"
#Nested
#DisplayName("GetStandingOrders()")
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
inner class GetStandingOrders {
#Test
fun `should return all StandingOrders`() {
standingOrderResource.getStandingOrders()
mockMvc.get(baseUrl)
.andDo { print() }
.andExpect {
status { isOk() }
content { contentType(MediaType.APPLICATION_JSON)}
}
//standingOrderResource.getStandingOrders() shouldBe listOf(standingOrderDto)
}
}
}
The problem is if i Make a API call or invoke the mocked repository it still gets actual data from DB
In your test code you should try to use method whenever() from org.mockito.kotlin for stubbing StandingOrderRepository's method call.
For example your code for stubbing will looks something like this
whenever(standingOrderRepository.findByNameAndVariableSymbol(any(),any())).thenReturn(listOf(StandingOrder(...)))
UPD: So you use Mockk, then you shuold use method every instead whenever from mockito.
So this is how i made it work maybe the issue was on my side how i was trying to use it #Anton Tokmakov was correct here is how i did it
#SpringBootTest
#AutoConfigureMockMvc
#ExtendWith(SpringExtension::class)
internal class StandingOrderResourceTest #Autowired constructor(
val mockMvc: MockMvc,
val objectMapper: ObjectMapper,
) {
#MockkBean
private lateinit var standingOrderResource: StandingOrderResource
#Nested
#DisplayName("GetStandingOrders()")
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
inner class GetStandingOrders {
#Test
fun `should return all StandingOrders`() {
every { standingOrderResource.getStandingOrders() } returns
listOf(standingOrderDto1, standingOrderDto2)
mockMvc.get(baseUrl)
.andDo { print() }
.andExpect {
status { isOk() }
content { contentType(MediaType.APPLICATION_JSON)}
}
.andExpect {
jsonPath("\$..[0]", match(MockMvcResultMatchers.content().json(Gson().toJson(
listOf(
standingOrderDto1,
standingOrderDto2
)), false)))
}
}
}

NullPointer with Reactive Spring Elastic Repository mocked into a service

This is the snippet of my test class:
#SpringBootTest
#ExtendWith(MockitoExtension::class)
class ResourceSearchServiceTest {
#Mock
lateinit var resourceSearchRepository: ResourceSearchRepository
#InjectMocks
lateinit var resourceSearchService: ResourceSearchService
lateinit var expectedResourceMetadata : ResourceSearchMetadata
#BeforeEach
fun createResourceMetadata() {
expectedResourceMetadata =
ResourceSearchMetadata(
id = UUID.randomUUID(),
pk = UUID.randomUUID(),
metadata = ResourceMetadata(
description = "blabla"
)
)
}
#Test
fun activeResourceMetadataTest() {
Mockito.`when`(resourceSearchRepository.findById(any(UUID::class.java))).thenReturn(Mono.just(expectedResourceMetadata))
Mockito.`when`(resourceSearchRepository.save(any(ResourceSearchMetadata::class.java))).thenReturn(Mono.just(expectedResourceMetadata))
resourceSearchService.updateActiveStatus(expectedResourceMetadata.id, false)
Mockito.verify(resourceSearchRepository, Mockito.times(1)).save(expectedResourceMetadata)
assertThat(expectedResourceMetadata.metadata.active).isFalse
}
}
and this is my service :
#Service
class ResourceSearchService(private val resourceSearchRepository: ResourceSearchRepository) {
fun updateActiveStatus(id: UUID, isActive: Boolean) {
resourceSearchRepository.findById(id).flatMap { resource ->
resource.metadata.active = isActive
resourceSearchRepository.save(resource)
}.map { Success }.defaultIfEmpty(NotFound)
}
}
when updateAciveStatus service method is called from the test class a nullpointer exception is raised by the repository at this line : resourceSearchRepository.findById(id)
It's like the mocked repository is not injected into the service.
Any idea what's wrong in my Kotlin code ?

How do I mock LocalDateTime() in Kotlin with Mockk

I'm writing test for a UserService that creates an anonymous user, and part of that includes saving a timestamp at time of creation. The time seems to be mocked properly within the test function itself, but when a timestamp is added in the actual UserService, the real time is being returned. How do I mock it properly?
the relevant UserService function is:
UserService.kt
fun createAnonymousUser(jwt: AuthenticationJsonWebToken): User {
val user = User()
user.anonUserId = jwt.name
user.createdAt = LocalDateTime.now(ZoneOffset.UTC)
return userRepository.save(user)
}
UserService.test.kt
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
open class UserServiceTest {
private val jwt = mockk<AuthenticationJsonWebToken>()
private val userRepository = mockk<UserRepository>()
private val authProperties = AllAuthProperties()
private val fixedClock = mockk<LocalDateTime>()
private val slotUser = slot<User>()
#BeforeTest
internal fun init() {
clearAllMocks()
}
#Test
fun getMe() {
every { jwt.name } returns "anonymous|123"
every { LocalDateTime.now() } returns LocalDateTime.MAX
val userService = UserService(userRepository, authProperties)
val user = User();
user.anonUserId = jwt.name
user.createdAt = LocalDateTime.now()
every { userRepository.save(capture(slotUser)) } returns user
val anonUser = userService.getMe(jwt)
verify { userRepository.save(anonUser) }
assertEquals(anonUser, user)
}
}
I'm just not sure how to accomplish what I'm trying.
Turns out I had it mostly right; I just needed a slight tweak.
verify { userRepository.save(anonUser.capture) }
assertEquals(anonUser, user)

Neo4J repository not wired everywhere

I've defined a Neo4j repository as follow (code is in Kotlin but it's very close to Java) :
#Repository
interface UserRepository : GraphRepository<User> {
fun findByEmail(email: String): User?
#Query("match (n:User)-[:IS_AUTH]->(:Permission {name: {0}}) where id(n) = {1} return n")
fun authorizedUser(permission: String, userId: Long): User?
}
In a controller, I've written:
#Controller
#RequestMapping("/company")
open class CreateCompanyController {
#Autowired private lateinit var userRepo: UserRepository
#RequestMapping(method = arrayOf(RequestMethod.POST))
#ResponseBody
fun createCompany(#RequestParam(value = "name", required = true) name: String,
#RequestParam(value = "siret", required = true) siret: Long) : ResponseEntity<String> {
val connectedUser = SecurityContextHolder.getContext().authentication.principal as User
val testUser = userRepo.findByEmail("test#company.com")
if (!PermissionManager().hasAuthorizationFor(PermissionManager.ADMIN_ALL, connectedUser))
return ResponseEntity("You're not authorized to create a company", HttpStatus.FORBIDDEN)
return ResponseEntity(HttpStatus.OK)
}
}
In the code above, the userRepo property is set well (aka. its value is not null).
I want to have this UserRepository in a custom service. So, I've coded:
#Service
open class PermissionManager {
companion object {
val ADMIN_ALL = "ADMIN_ALL"
}
#Autowired private lateinit var userRepo: UserRepository
fun hasAuthorizationFor(permissionName: String, user: User): Boolean {
return userRepo.authorizedUser(permissionName, user.id!!) != null
}
}
But in this case, the userRepo property is not initialized.
I've written the PersistenceContext class as:
#Configuration
#ComponentScan("persistence")
#EnableNeo4jRepositories("persistence.repositories")
#EnableTransactionManagement
open class PersistenceContext : Neo4jConfiguration() {
#Bean override fun getSessionFactory(): SessionFactory {
return SessionFactory("persistence.domain")
}
#Bean
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
open override fun getSession(): Session {
return super.getSession()
}
}
What I don't understand is why is the userRepo set in my controller but not in my service ?

Resources