Mock a Service class with dependencies in another Service Class Mockito - spring-boot

I want to Mock a Service class with dependencies in another Service Class Mockito.
UserService Class
#Service
class UserService(
val userRepository: UserRepository,
val userRoleRepository: UserRoleRepository,
val jwtGenerator: JwtGenerator,
val emailService: EmailService
)
AdminService Class
#Service
class AdminService(
val userService: UserService,
val userRepository: UserRepository,
val adminRepository: AdminRepository
)
Now I'm trying to write unit test with these classes as follows
#RunWith(MockitoJUnitRunner::class)
class AdminServiceTests {
private lateinit var createAdmin: CreateAdmin
#Mock
lateinit var userRepository: UserRepository
#Mock
lateinit var userRoleRepository: UserRoleRepository
#Mock
lateinit var jwtGenerator: JwtGenerator
#Mock
lateinit var emailService: EmailService
#InjectMocks
lateinit var userService: UserService
#Mock
lateinit var userRepository: UserRepository // Conflicting declarations
#Mock
lateinit var adminRepository: AdminRepository
#InjectMocks
lateinit var adminService: AdminService
#Before
fun setup() {
this.createAdmin = CreateAdmin().apply {
email = "admin#gmail.com"
name = "admin"
password = "qwerty"
phone = 9873555555555
}
}
#Test
fun testCreateAdmin() {
val result = adminService.createAdmin(createAdmin)
Assert.assertEquals(true, result)
}
}
Please advice how to mock the userService inside AdminService with all the four dependencies injected

You can create object of UserService with its dependencies as mocked objects and then use it as a dependency in AdminServiceTests.

If you're using Gradle & Spring, add this plugin to your build.gradle
plugins {
kotlin("plugin.spring") version "1.3.72"
}
Mockito should then work without having to mock the dependencies of the service:
val userService = Mockito.mock(UserService::class.java)

Related

Mocking autowired dependencies with Mockito

I'm writing unit tests for a Spring project with Junit 5 and Mockito 4.
I have to test a class that takes 2 objects via constructor and other 2 via #Autowired. I need to mock those 4 objects, so I annotated them with #Mock in my test class and then annotated the tested class with #InjectMocks.
I thought that #InjectMocks would inject my 4 mocks into myService, but it's only injecting the 2 that are passed by constructor, while the other 2 are null.
I'm looking for a solution that doesn't implies changes in the tested service.
The tested class looks like this:
#Service
public class MyService {
private String key = "KEY";
#Autowired
private FirstApiWrapper firstApiWrapper;
#Autowired
private SecondApiWrapper secondApiWrapper;
private MyRepository myRepository;
private OtherService otherService;
#Autowired
public MyService(
MyRepository myRepository,
OtherService otherService
) {
super();
this.myRepository = myRepository;
this.otherService = otherService;
}
My test class looks like this:
#ExtendWith(MockitoExtension.class)
public class MyServiceTest {
#Mock
MyRepository myRepository;
#Mock
OtherService otherService;
#Mock
FirstApiWrapper firstApiWrapper;
#Mock
SecondApiWrapper secondApiWrapper;
#InjectMocks
MyService myService;
Any ideas of what is wrong with my code?
Thank you all very much!
-- I've also tried something based on this question:
#Mock
FirstApiWrapper firstApiWrapper;
#Mock
SecondApiWrapper secondApiWrapper;
#InjectMocks
MyService myService;
#BeforeEach
private void setUp() {
myService = new MyService(
Mockito.mock(MyRepository.class),
Mockito.mock(OtherService.class)
);
}
But the result is exactly the same. Also, if I delete repository and service instances and try to inject only the wrappers, It still fails!
I found a way to solve it without rewriting the existing code, by adding this to the test class:
#BeforeEach
private void setUp() {
MockitoAnnotations.openMocks(this);
}
But I'm not sure if it is a "correct" way of doing it.
Once of the issues with fields autowiring is that mockito won't be able to inject anything. So why do you need to mix the styles of injection if you already have a constructor-injection?
Rewrite your class:
#Service
public class MyService {
private String key = "KEY";
private final MyRepository myRepository;
private final OtherService otherService;
private final FirstApiWrapper firstApiWrapper;
private final SecondApiWrapper secondApiWrapper;
#Autowired // if its the only constructor in the class, you can omit #Autowired, spring will be able to call it anyway. You can even use Lombok to generate this constructor for, so you won't need to even write this method
public MyService(
MyRepository myRepository,
OtherService otherService,
FirstApiWrapper firstApiWrapper,
SecondApiWrapper secondApiWrapper
) {
this.myRepository = myRepository;
this.otherService = otherService;
this.firstApiWrapper = firstApiWrapper;
this.secondApiWrapper = secondApiWrapper;
}
With this design you can safely use #Mock / #InjectMocks annotations in the test
Mockito will create the instance of the class and inject relevant mocks.

Kotlin spring controller integration test not working

struggling here, I have been stuck on this for about a week now, don't know how to get this working. I am trying to write a integration test to check if my introduced spring variable validation in my controller is working, but I am running into issues on how to exactly do this in kotlin.
With where I am now, I have all the dependencies autowired, and can pass the #AuthenticationPrincipal layer of my controller and the code executes, but if I debug, the userService which is being used for user retrieval, and has been autowired is null in the controller(not NPE but just null when debugging inside of the controller). It seems there is an issue with wiring in that service/dependency?
I have also tried the mvc.StandAloneSetup(UserController) and have gotten that to work with all the dependencies, but unfourtunately I had to abandon that approach, because I could not figure out how pass the #AuthenticationPrincipal.. #WithUSerDetails, #WithMockUser, #WithUser, none of which worked. Any advice onw hat I am doing wrong? New to kotlin so don't know much
Additionally I had some success with mappings that did not require authenticaiton, but the validation seemed to always spout random errors(error from previous test execution) issue with context clearing?
#RunWith(SpringJUnit4ClassRunner::class)
#SpringBootTest
#AutoConfigureMockMvc
#ActiveProfiles("test")
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
open class UsersControllerTest {
#Autowired lateinit var mockMvc: MockMvc
#Autowired lateinit var mapper: ObjectMapper
#Autowired private var db = testDatabase()
#Autowired private lateinit var userRepository: UserRepository
#Autowired private lateinit var dependency : dependency
#Autowired private lateinit var usersService : UsersService
#Autowired private lateinit var someUserService : someUserService
#Autowired private lateinit var userController: UsersController
#MockBean private lateinit var userPrincipal: UserPrincipal
#Autowired private lateinit var context: WebApplicationContext
#BeforeEach
fun init(){
dependency = Dependency()
userRepository = UserRepository(db)
userRepository.insert(validUser)
usersService = UsersService(userRepository,dependency,"var")
someUserService = SomeUserService(usersService)
userController = UsersController(usersService,someUserService,dependency)
userPrincipal = UserPrincipal(validUser, null)
this.mockMvc = MockMvcBuilders.webAppContextSetup(context).build()
}
#AfterEach
open fun cleanup() {
SecurityContextHolder.clearContext()
}
#Test
#WithUserDetails("admin",serviceBeanName = "userService", setupBefore = TestExecutionEvent.TEST_EXECUTION)
fun getUsers() {
val result = mockMvc.perform(
MockMvcRequestBuilders.get("/users/",{false}).accept(MediaType.APPLICATION_JSON).contentType(
MediaType.APPLICATION_JSON
)
).andExpect(MockMvcResultMatchers.status().isOk)
.andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultHandlers.print())
.andReturn()
}
Controller:
#RestController
#Validated
#CrossOrigin(origins = ["*"])
#RequestMapping("${API_ROOT}users/")
open class UsersController #Autowired constructor(
private val usersService: UsersService,
private val someService: SomeService,
private val dependency: dependency,
) {
companion object {
fun validatePermissions(principal: UserPrincipal, userId: UserId) {
if (principal.user.id != userId &&
...
}
#GetMapping
#RolesAllowed(ROLE_ADMIN)
fun getAllUsers(
#AuthenticationPrincipal principal: UserPrincipal,
#RequestParam(value = "admin", required = false) includeAdmin: Boolean = false,
#RequestParam(value = "guest", required = false) includeGuest: Boolean = false,
): List<User> {
validatePermissions()
return usersService.getAll()
}

Kotlin + Mockito + Spring-boot

I am studying Kotlin with Spring Boot, and I am trying to use Serenity just for report.
But I have some problem with Mockito because I cannot mock the last part of my code.
That's my case:
#RestController
#RequestMapping("/person")
class PersonController {
#Autowired
private lateinit var personUseCase : PersonUseCase
#GetMapping("/all")
fun findAllPeople(): DataModelResponse<List<PersonDataModelResponse>> {
return DataModelResponse(
PersonDataModelResponseMapper.transform(personUseCase.findAll()))
}
}
#Component
class PersonUseCase {
fun findAll(): List<PersonEntity> {
val personImpl : Person = PersonImplementation()
return personImpl.findAll()
}
}
class PersonImplementation : Person {
private val personDaoResponse : PersonDaoResponse = PersonDaoResponse()
override fun findAll(): List<PersonEntity> {
val listPeopleDao = personDaoResponse.findAll()
return PersonDaoMapper.transform(listPeopleDao)
}
}
internal class PersonDaoResponse (
val identification: Long = 0,
val personName: String = "") {
fun findAll(): List<PersonDaoResponse> {
return listOf(PersonDaoResponse(1, "José"))
}
}
And I am trying to mock my Dao in an integration test:
#RunWith(SerenityRunner::class)
#WithTag("Integration")
#AutoConfigureMockMvc
#WebMvcTest
class PersonControllerTest {
#InjectMocks
private lateinit var personController : PersonController
#InjectMocks
private lateinit var personImplementation : PersonImplementation
#SpyBean
private lateinit var personUseCase : PersonUseCase
#Spy
private val personDaoResponse = PersonDaoResponse()
#Autowired
private lateinit var webApplicationContext: WebApplicationContext
#Autowired
private lateinit var mockMvc: MockMvc
#Rule #JvmField
var springMethodIntegration = SpringIntegrationMethodRule()
#Before
fun init() {
MockitoAnnotations.initMocks(this)
reset(personDaoResponse)
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build()
this.mockMvc = MockMvcBuilders.standaloneSetup(personController).setMessageConverters(MappingJackson2HttpMessageConverter()).build()
}
#Test
fun `integration success`() {
doReturn(listOf(PersonDaoResponse(999L, "Zé")))
.`when`(this.personDaoResponse).findAll()
val result = this.personController.findAllPeople()
assertNotNull(result)
assertEquals(999L, result.data[0].personId)
assertEquals("Zé", result.data[0].name)
}
}
I know this mock is working if I do something I try call the mock method:
assertEquals(999L, this.personDaoResponse.findAll()[0].identification)
How can I Mock this Dao in an integration test like this?
Unfortunately, I don't have an answer for the Mockito problem because I've been off Mockito for way to long.
If Mockito is not a must, I recommend using MockK (https://github.com/mockk/mockk) from my Spring Boot + Kotlin experience. If you need injectable mocked Beans as well, you can add SpringMockK (https://github.com/Ninja-Squad/springmockk) as well.
As your "PersonDaoResponse" is constructed within the PersonImplementation class, you could use something like this with MockK:
every { anyConstructed<PersonDaoResponse>().findAll() } returns listOf(PersonDaoResponse(999L, "Zé"))

How to test controller with auth via jwt

I'm new in Spring. I need to know how to test my controller:
#RestController
#RequestMapping("/api/game")
class GameController {
#Autowired
lateinit var userRepository: UserRepository
#Autowired
lateinit var gameRepository: GameRepository
#Autowired
lateinit var gameService: GameService
#PostMapping("/start")
fun start(#CurrentUser currentUser: UserPrincipal): Game {
val user = userRepository.findById(currentUser.id!!).get()
return gameService.createGame(user)
}
}
I use jwt token to auth, and I cannot test my controller without it now. How to do it? How to mock it?

Call a function from Test class not working Mockito

Hi I have a test class named UserServiceTest which contains userService class which is injected with the above mocks and the tests in this test class works fine.
#RunWith(MockitoJUnitRunner::class)
class UserServiceTest {
lateinit var login: Login
#Mock
lateinit var userRepository: UserRepository
#Mock
lateinit var emailService: EmailService
#InjectMocks
lateinit var userService: UserService
#Before
fun setup() {
login = Login(email = "mohanraj#gmail.com", password = "qwerty"
}
And I have another test class named AdminServiceTests which contains the AdminServiceClass which is injected with the above mocks which also consists the userService class
#RunWith(MockitoJUnitRunner::class)
class AdminServiceTests {
lateinit var user: User
#Mock
lateinit var userRepository: UserRepository
#Mock
lateinit var adminRepository: AdminRepository
#Mock
lateinit var userService: UserService
#InjectMocks
lateinit var adminService: AdminService
#Before
fun setup() {
this.createAdmin = CreateAdmin().apply {
email = "admin#gmail.com"
name = "admin"
password = "qwerty"
phone = 98345678899
}
}
#Test
fun testCreateAdmin() {
val result = adminService.createAdmin(createAdmin)
Assert.assertEquals(true, result)
}
When I run the test, adminService.createAdmin(createAdmin) calls a function in the adminService which calls a function in userService
fun createAdmin(newUser: CreateAdmin): Boolean {
val user = userService.getUser(newUser)
if (userService.createUser(user)) { // calls a function in user service
this.saveAdmin(user.id)
return true
}
return false
}
I want to call a function in userService from testCreateAdmin function, but the user service which I have included as #Mock contains some dependencies for itself and it is a dependency for adminService, so when I debug the #Mock userService object contains null and so the function in userService is not called. so how to inject the dependencies to userService and inject it into adminService.. Please help
userService = {UserService$MockitoMock$825690330#2479}
mockitoInterceptor = {MockMethodInterceptor#2532}
userRepository = null
emailService = null
In your testCreateAdmin() you have to mock the behavior of the userService methods. By default, non-mocked methods will return null.
It should look like this, with mockito-kotlin.
#Test
fun testCreateAdmin() {
val admin = mock<User>() {
on { id } doReturn "id"
}
whenever(userService.getUser(createAdmin)).doReturn(admin)
whenever(userService.createUser(admin)).doReturn(true)
val result = adminService.createAdmin(createAdmin)
Assert.assertEquals(true, result)
verify(userService).saveAdmin("id")
}

Resources