NullPointer with Reactive Spring Elastic Repository mocked into a service - spring

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 ?

Related

How to initialize repository from service in Springboot Kotlin

#Service
public class AvailablePolicyService {
#Autowired
private var availablePolicyRepository : AvailablePolicyRepository = **AvailablePolicyRepository()**
fun saveAvailablePolicy(availablePolicy: AvailablePolicy): AvailablePolicy { return availablePolicyRepository.save(availablePolicy) }
fun getAllAvailablePolicy(): List<AvailablePolicy>{ return availablePolicyRepository.findAll() }
fun getAvailablePolicyByPolicyId(policyId: String?): AvailablePolicy? {
var availablePolicies: List<AvailablePolicy> = getAllAvailablePolicy()
for (availablePolicy in availablePolicies) {
if (availablePolicy.getPolicyId().equals(policyId)) {
return availablePolicy
}
}
return null
}
fun getAvailablePolicyByPolicyCategory(policyCategory: String?): ArrayList<AvailablePolicy> {
var availablePolicies: List<AvailablePolicy> = getAllAvailablePolicy()
var availablePolicyCategory = ArrayList<AvailablePolicy>()
for (availablePolicy in availablePolicies) {
if (availablePolicy.getPolicyCategory().equals(policyCategory)) {
availablePolicyCategory.add(availablePolicy)
}
}
return availablePolicyCategory
}
}
#Repository
interface AvailablePolicyRepository : MongoRepository<AvailablePolicy, String>
The bolded text shows where the error is showing and it reads "Interface AvailablePolicyRepository does not have constructors". How do I initialize repository from service?
How do I initialize repository from service?
That's the thing. You don't! Spring does it for you:
#Autowired
private lateinit var availablePolicyRepository: AvailablePolicyRepository
Field injection is rather obsolete and you should consider using contructor injection instead.
#Service
class AvailablePolicyService(private val availablePolicyRepository: AvailablePolicyRepository) {...}

Spring test #Transactional with #Nested junit classes

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.

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)))
}
}
}

lateinit property S has not been initialized

I want inject a singleton in another class by kotlin in spring boot.
S.kt
#Singleton
#Component
class S(
private val userService: UserService,
val companyRepo: CompanyRepo
)
WorkingGroup.kt
class WorkingGroup(
override val name: String = "",
override val desc: String = ""
) : Csv() {
fun isCompatible(ct2: WorkingGroup): Boolean = this == ct2
companion object : ICsvEnumCompanion<WorkingGroup> {
#Inject
private lateinit var s: S
override val VALUES: List<WorkingGroup>
by lazy {
val details = s.user().company.details ?: CompanyDetails()
details.workingGroups.map { WorkingGroup(it.name, it.desc) }
}
}
}
By this code, I get below error:
Caused by: org.zalando.problem.DefaultProblem: Internal Server Error: lateinit property s has not been initialized
I search for this error and found some result like this, but the problem not solved.
How can I inject service in companion object in kotlin?
In order for Spring to inject into a companion object you will need to create a setter for the field outside of the companion object. WorkingGroup will need to be a Spring managed bean in order for Spring to autowire it (inject dependencies).
#Component
class WorkingGroup(
override val name: String = "",
override val desc: String = ""
) : Csv() {
fun isCompatible(ct2: WorkingGroup): Boolean = this == ct2
companion object : ICsvEnumCompanion<WorkingGroup> {
private lateinit var s: S
override val VALUES: List<WorkingGroup>
by lazy {
val details = s.user().company.details ?: CompanyDetails()
details.workingGroups.map { WorkingGroup(it.name, it.desc) }
}
}
#Autowired
fun setS(value: S) {
s = value;
}
}

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