How to mock a method in Service layer - spring-boot

While doing unit tests on each method of the service layer, I have encountered the below scenario, that I couldn’t figure out how to test:
public class UserServiceImpl{
#Autowired
UserRepository userRepository;
public void abc(){
xyz(obj);
}
private void xyz(){
userRepository.save(obj);
}
}
What I want to test is the abc() method. Within that method, it invokes xyz() which is a PRIVATE method that uses the userRepository dependency. So, when I create a unit test for the abc() method, do I need to be concerned about the xyz() method since it is using a dependency? And if yes, what are the steps that I need to follow?

As you wrote you need to deal with xyz() method and its call to userRepository. You need to mock userRepository as follows:
#ExtendWith(MockitoExtension.class)
public class UserServiceImplTest {
#Mock
private UserRepository userRepository;
#InjectMocks
public UserServiceImpl userService;
#BeforeEach
public void setUp() throws Exception {
// Mock UserRepository behaviour
doReturn(//return value).when(this.userRepository).save(any());
}
// Your tests here
}

Since this is a void method, what you want to do is verify that the save method of the dependency has been called exactly once with the parameter obj. You could do this by using something like Mockito. Your unit test would look something like this:
#Mock
private UserRepository mockUserRepository;
#InjectMocks
private UserServiceImpl sut;
#Test
public void abc_savesObject() {
// Arrange
...
// Act
sut.abc();
// Assert
verify(mockUserRepository,times(1)).save(obj);
}
Some useful links:
https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#4
https://www.baeldung.com/mockito-verify

Related

How to verify method, which has transactional lines, in unit test for Spring Boot Service?

i Have following UserServiceImpl Service Class:
#Service #Transactional
public class UserServiceImpl implements UserDetailsService {
...
public void addRoleToUser(String username, String name) {
User user = this.userRepository.findByUsername(username);
Role role = this.roleRepository.findByName(name);
user.getRoles().add(role);
}
}
and it's corresponding test class:
public class UserServiceImplTest {
#Mock private UserRepository userRepository;
#Mock private RoleRepository roleRepository;
private AutoCloseable autoCloseable;
private UserServiceImpl userServiceImpl;
#BeforeEach
void setUp(){
autoCloseable = MockitoAnnotations.openMocks(this);
userServiceImpl = new UserServiceImpl(userRepository, roleRepository);
}
#AfterEach
void tearDown() throws Exception{
autoCloseable.close();
}
#Test
void testAddRoleToUser() {
... // user and role def. here
userServiceImpl.addRoleToUser(username, roleName);
// what should be here?
}
}
But how can I verify that user.getRoles().add(role); is invoked in unit test? Is using mockito, and ArgumentCaptor enough?
Thank you.
Since userRepository is a mock, you probably define a behaviour for the findByUsername call and return an object there. The object is created in the test method (in the "given" block), so you have access to it after calling the method (in the "then" block). You can simply verify if the object contains a given role like so (AssertJ ussage assumed):
assertThat(user.getRoles())
.contains(role);
roleRepository is also a mock, so the role object is probably also available in the test. If it was not the case, information about the role could be extracted from the asserted object(s) and verified (for example a name as a String).

Testing Conditionally Initialized RestController based on active profile

I have the following class, which is loaded conditionally based on the profile. I want it to only load for assisting in lower environments and not production:
#RestController
#RequestMapping("/foo")
#Profile("!prod")
public class FooController {
private Bar bar;
#PostMapping
public ResponseEntity<?> createFoo(){
bar.something();
return new ResponseEntity<Object>(HttpStatus.ACCEPTED);
}
}
I would like to have the following tests for this class:
#WebMvcTest
#RunWith(SpringRunner.class)
#ActiveProfiles("prod")
public class FooControllerTest {
private MockMvc mockMvc;
#Mock
private Bar bar;
#InjectMocks
private FooController subjectUnderTest;
#Before
public void init(){
mockMvc = MockMvcBuilders
.standaloneSetup(subjectUnderTest)
.build();
}
#Test
public void givenProdProfile_whenCreateFoo_thenNotFoundResponseReturned() throws Exception {
doNothing().when(mockBar).something();
mockMvc.perform(MockMvcRequestBuilders
.post("/foo"))
.andExpect(status().isNotFound());
}
and then another test class to test this case, with #ActiveProfile('non-prod'):
givenNonProdProfile_whenCreateFoo_thenAcceptedResponseReturned
I am confused on how to properly test this. I've tried many different different approaches, and none of them seem to properly test the class. I've tried annotating my test class with #ActiveProfile, etc.
I suppose the crux of my question, is how can I test different profiles with MockMvc and Mockito?

Null is passed to autowired service even after mocking - SpringBootTest

I wanted to do integration testing of my API.
#RestController
#RequestMapping("/api/v1")
public class TestController {
#Autowired
TestService testService;
#RequestMapping("/welcome")
public String welcomeMessage(#RequestParam("name") String name) {
return testService.welcomeMessage(name);
}
}
Below are the service interface and its implementation:
public interface TestService {
public String welcomeMessage(String name);
}
public class TestServiceImpl implements TestService{
#Autowired
TestRepository repo;
#Override
public String welcomeMessage(String name) {
repo.save(new StringEntity(name));
return "Hello "+name;
}
}
Below is the Test Case:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class MockitoTestingApplicationTests {
#Autowired
MockMvc mvc;
#MockBean
TestService testService;
#MockBean
TestController testController;
#Test
public void contextLoads() throws Exception {
Mockito.when(testController.welcomeMessage(ArgumentMatchers.anyString())).thenCallRealMethod();
Mockito.when(testService.welcomeMessage(ArgumentMatchers.anyString())).thenCallRealMethod();
mvc.perform(get("/api/v1/welcome").param("name", "dude")).andExpect(status().isOk());
}
}
I have a few questions.
when I'm executing the above code it is throwing an error saying cannot call real method on abstract methods. And When I'm mocking the TestServiceImpl, It is throwing NullPointerException in the Controller because the TestService is null. How should I fix that?
How should I mock the repository layer when we are using MongoDB. when I try to Mock MongoTemplate, It is throwing an error saying MongoConvertor must not be null
Is this the right way to write test cases. can we have code coverage without using thenCallRealMethod()?
Please suggest me how to proceed. Thanks in advance.
Make sure you have an implementation of the service i.e. TestServiceImpl annotated with #Service (or #Component if it is not strictly a service) and use spying instead of mocking:
#SpyBean
TestService testService;
Spying by default call real methods so you have to mock these that implementation you do not want to call.
Regarding repositories, you should mock the components annotated with #Repository, not the actual SessionFactory / Template etc. that are used within.

Mock an object which is part of mocked object

when I run the test case I get AnObj mocked. this is used from inside the target classes method. when that method gets invoked the 'anOtherObj' is accessed and that is found to be null. Can some one please point out how to make sure 'anOtherObj' is not null so that I dont get nullpointer there?
#Test
public class TestTargetTest {
#Mock
private AnObj anObj;
#InjectMocks
private TestTarget testTarget;
#BeforeMethod
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void testTarget() {
when(anObj.someMethod()).thenCallRealMethod();
testTarget.testTarget();
}
}
#Component
public class TestTarget {
#Autowired
private AnObj anObj;
public void testTarget(){
anObj.someMethod();
}
}
#Component
public class AnObj {
#Autowired
private AnOtherObj anOtherObj;
public void someMethod(){
syso(anOtherObj.toString());
}
}
You need to initialize annotated mocks in your test class.
#BeforeMethod
public void beforeClass() {
MockitoAnnotations.initMocks(this);
}
Why would you care what's inside mock (AnObj)? I assume you haven't yet declared interactions on that mock using Mockito.when.
As mentioned by #Valya that point was valid. I shouldn't have mocked that. I needed to autowire 'AnObj'. Thanks a lot for all the help. It made the difference.

Mockito - Accessing private/autowired fields for verify()

So I am new to Mockito for testing facades. So basically I want to check, if a method of Service gets called once.
Here would be a simplified example
My Service
public class Service {
public int myMethod(int index, int number) {
if (index<4){
index = index + number;
}
return index;
}
}
My Facade:
public class Facade {
private Service service;
public void method(){
int i = service.myMethod(4, 2);
}
}
and finally my Test:
public class FacadeTest {
#InjectMocks
private Facade classUnderTest;
#Mock (name="service")
private Service service;
#Before
public void setUp(){
MockitoAnnotations.initMocks(this);
}
#Test
public void test(){
verify(classUnderTest, times(1)).service.myMethod(4,2);
}
}
I know it is possible to use Getter/Setter-methods in my Facade to return the Service, but I want to do it, without doing that.
Is it possible, in the way I want to, without doing any change to the Facade?
And is there a difference, when I have a Spring-project and used #Autowired for the service variable inside the Facade?
Thanks!
Using #InjectMocks will inject your annotated #Mock service in your facade.
So you don't need any getter or setter for your test.
It seems you forgot to call method in your test.
Try with this :
#Test
public void test(){
classUnderTest.method();
verify(service, times(1)).service.myMethod(4,2);
}
Using #Autowired service in your facade will have no impact on your test since spring is not used in it. Spring will inject the right bean when you'll run your application.

Resources