Mockito #Spy calls real method while mocking behaviour is defined - spring

I have a common spring boot application consisting of a controller and a service layer. A database is not involved as I use another rest api to store the data.
Now I want to the my controller and therefor I want to mock my sevice partially. Partially because I have one method in it that takes a dto and converts it to my business model. I know this can also be solved with a constructor of the bussiness model but anyway I came to the following problem:
CODE
Controller
#RestController
public class RegistrationController {
#Autowired
private UserRegistrationService userRegistrationService;
#PostMapping(value = "/user", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<UserId> createUser(#RequestBody #Valid UserDto userDto) {
KeycloakUserRepresentation keycloakUserRepresentation = userRegistrationService.convertUserDtoToKeycloakUserRepresentation(userDto);
UserId userId = userRegistrationService.createNewUser(keycloakUserRepresentation);
return new ResponseEntity<>(userId,HttpStatus.CREATED);
}
TEST
#SpringBootTest
#AutoConfigureMockMvc
#ExtendWith({RestDocumentationExtension.class})
#AutoConfigureRestDocs
class RegistrationControllerRegistrationTest {
private static final UserDto testUsertDto = new UserDto();
#Autowired
private MockMvc mockMvc;
#Autowired
private ObjectMapper objectMapper;
#Spy
private UserRegistrationServiceImpl userRegistrationService;
In my test method I define:
doReturn(testUserId).when(userRegistrationService).createNewUser(any(KeycloakUserRepresentation.class));
PROBLEM:
I expect that while not define a doSomething in my test, the conversion of the userDto to a keycloak representation is done by the original method. This seems to work as when I debug in my Controller the keycloakUserRepresentation has the correct values. The problem is that in the next step the
createNewUser
method is not stubbed/mocked. The original method is executed and so my test fails.
What I want is that when I provide a doSomething method in my testcase, I want the original method to be mocked.

This seems to work as when I debug in my Controller the
keycloakUserRepresentation has the correct values.
It worked because the bean wasn't spied on at all. Although #Spy may help in creating a mocked object, it does not result in a mocked Spring bean in your test application context. Use #SpyBean to have Mockito spy on a bean in the Spring application context.

Related

how to use junit and mockito when we have a mulit layer service call approach in a restfull web service

i am using spring tool suite to write a code .there are 4 layers restContoller,buisnesslogic,domain ,service....
i want to test for a method of business logic layer where it calls a method of dao which finally calls a method of service layer to return a simple primitive value... to make it clear in the businesslogic class i have autowired domain class ,and in the domain class i have autowired the service classs..the problem that i am facing iss when i run the test class i am getting NullPointerException i am attaching the code for the test class... kindly help if possible
#ExtendWith(MockitoExtension.class)
class CustomerBlTest {
#Mock
CustomerService mockService;
#Autowired
CustomerDO customerDo;
#Autowired
#InjectMocks
CustomerBl bl; //buisnesslogic class
#Test
void checkForGetInteger() {
when(mockService.getIntegerFfromService()).thenReturn(3);
int actual = bl.getInteger();
Assertions.assertEquals(3, actual);
}
}
Since you are extending MockitoExtension hence this test class is not aware of spring. But you are still using #Autowired annotation. So that's wrong. Remove all #AUtowired annotations in the test class. Besides this you do not need to bring in all the sterotyped classes. Bring in only the one that the class is using i.e. in your case the classes injected in CustomerBl class. I think that should be CustomerService class. So remove the CustomerDO class if it's not being used in CustomerBl class. The #InjectMock and #MOck annotation have been applied correclty. I think that should help you get your result.
You need to use #Mock instead of #Autowired as shown below.
#ExtendWith(MockitoExtension.class)
class CustomerBlTest {
#Mock
CustomerService mockService;
#Mock
CustomerDO customerDo;
#InjectMocks
CustomerBl bl; //buisnesslogic class
#Test
void checkForGetInteger() {
when(mockService.getIntegerFfromService()).thenReturn(3);
int actual = bl.getInteger();
Assertions.assertEquals(3, actual);
}
}

What is sense of #Mock annotation?

I have a question about bean creation in testing of controllers. For example, there is a such test
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {MainApplicationConfiguration.class, JPAConfig.class})
#WebAppConfiguration
public class TestMainController {
private MockMvc mockMvc;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(mainController).build();
}
#InjectMocks
private MainController mainController;
#Mock
private EntryService entryService;
#Autowired
DBEntryRepository repository;
#Test
public void testEntryGet() throws Exception {
List<DBEntry> response_data = new ArrayList<>();
response_data.add(new DBEntry(1, 1, "STR", "DATE"));
Mockito.when(entryService.findAllEntries())
.thenReturn(response_data);
MvcResult result = mockMvc.perform(get("/VT/entry/"))
.andExpect(status().isOk()).andReturn();
verify(entryService, times(1)).findAllEntries();
verifyNoMoreInteractions(entryService);
}
}
and a controller method mapped on
/VT/entry/
#RequestMapping(value = "/entry/", method = RequestMethod.POST)
public ResponseEntity<Void> createEntry(#RequestBody DBEntry entry, UriComponentsBuilder ucBuilder) {
System.out.println("Creating entry " + entry.getNum());
try {
entryService.saveEntry(entry);
entryService.refreshEntryService();
} catch (Exception e) {
e.printStackTrace();
return new ResponseEntity<Void>(HttpStatus.BAD_REQUEST);
}
HttpHeaders headers = new HttpHeaders();
headers.setLocation(ucBuilder.path("/entry/{id}").buildAndExpand(entry.getId()).toUri());
return new ResponseEntity<Void>(headers, HttpStatus.CREATED);
}
EntryService is annotated with #Service annotation and MainApplicationConfiguration.class is a configuration with #EnableWebMvc and scan project for this EntryService.
by that I want to show that this controller really uses this EntryService in a real application and all are coupled by MainApplicationConfiguration.class.
Question is: Why entryService with #Mock annotation ended up in my controller code in the scope of my test execution? Shouldn't it be only for that instance only and inside of controller should be instantiated another bean(EntryService), why this annotation has mocked all occurrences of that bean (in the test scope)? I was thinking, that I should write whole other context web-context instead of MainApplicationConfiguration.class to mock it inside and substitute current definition. I am absolutely confused why this simple annotation have made such thing.
And if somebody can understand this magic, please say what is difference between #InjectMock and #Mock?
thanks for your attention! and sorry if my question is quite stupid. I am very new, it works, but I have not got magic yet.
In the documentation for #InjectMocks:
Mockito will try to inject mocks only either by constructor injection, setter injection, or property injection in order
So since EntryService is a dependency of your controller, #InjectMocks will try to find a mock object of EntryService in your test class and inject it into mainController.
Note that only one of constructor injection, setter injection, or property injection will occur.
#Mock marks the fields as mock objects.
#InjectMocks injects mock objects to the marked fields, but the marked fields are not mocks.

Spring - Is it good to register request scoped bean at every request though code?

In Spring Web application, I have to use specific value from request object in another spring classes within application. Value is request specific value.
In the following example, is it good way to register value coming from Request Body every time and use #Autowired with #RequestScope annotation to use value in another spring(e.g. #Service) classes? Is it good to register RequestScopedType bean value for each request through BeanFactory?
#RestController
#RequestMapping("/")
public class VehicleServiceController {
#Autowired
private BeanFactory beanFactory;
#Autowired
private ServiceClass serviceClass;
#PostMapping(path = "/postDetails", consumes = MediaType.APPLICATION_JSON_VALUE)
public OutputPayload postDetails(
#RequestBody InputPayload inboundPayload) throws Exception {
beanFactory.getBean(RequestScopedType.class).setValue(inboundPayload.getType());
return serviceClass.methodToCall();
}
}
Will there be any impact on performance as load is very huge? Is there any another way to inject/get RequestBody object value(inboundPayload.getType())?
You don't have to do beanFactory.getBean(RequestScopedType.class). You can just simply autowire it #Autowired RequestScopedType requestScopedType.
Just don't forget to change the scope of the bean as Request.
#Component
#Scope(scopeName = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestScopedType {
But this begs another question, why over complicate things, why can't you pass inboundPayload.getType() to the serviceClass.methodToCall(); ?
What is stopping you from using it this way return serviceClass.methodToCall(inboundPayload.getType());

How to call Springs service method from controller (Junit)

I have seen example , how to call spring controller using mockito.
Using Mock I call Spring MVC controller.
Controller Invokes Spring service class.
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/root-context.xml" })
public class TestController {
#Mock
private TestService testService;
#InjectMocks
private PaymentTransactionController paymentController;
private MockMvc mockMvc;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.setMockMvc(MockMvcBuilders.standaloneSetup(paymentController).build());
}
#Test
public void test() throws Exception {
this.mockMvc.perform(post("/tr/test").content(...)).andExpect(status().isOk());
// testService.save(); <-- another way
}
Ok it works well. I calls my Spring controller very well. But In Spring controller I have Injected Service Layer.
#Autowired
private TestService serviceTest;
#RequestMapping(value = "/test", method = RequestMethod.POST)
#ResponseBody()
public String test(HttpServletRequest request) {
...
serviceTest.save();
// in save method I call dao and dao perist data;
// I have injected dao intrface in serviceTest layer
...
return result;
}
The problem is that, my app does not invokes save method, it is not entered in it. I have no error too. The same result is when I call save() method from Junit (I have commented it in test() method).
When I debug, I have seen that interrupt method happens of org.mockito.internal.creation.MethodInterceptorFilter
How to solve this problem? what happens?
If you are doing a unit test of your controller, you should mock the service layer (what you are doing). In this kind of test, you just control that :
the correct methods of the controller are triggered and they produce what is expected
the correct methods in service layer are called ... in the mock
You simply have to configure the return values of the methods of the mock (if relevant), or control what was called
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.setMockMvc(MockMvcBuilders.standaloneSetup(paymentController).build());
// set return values from the mocked service
when(testService.find(1)).thenReturn(...);
}
and verify later what has been called
#Test
public void test() throws Exception {
this.mockMvc.perform(post("/tr/test").content(...)).andExpect(status().isOk());
// testService.save(); <-- another way
verify(testService, times(1)).save();
}
If you want to do an integration test, you do not mock the service, but setup an application context to inject real beans, but ordinarily use an embedded database instead of the real one.
just change #InjectMocks to #Autowired. This fix the issue! In this case you are not mocking, you are invoking method with real data.
As I understand, you perform post to "/tr/test" resource, but request mapping in your controller is '/payment'. Make sure you post to resource mapped in the controller.

Can I mock only one dependency of my controller

I have a controller with multiple dependency which are solved by using spring configuration and Autowired in the controller class.
For Example:
#Controller
public class MyController{
#Autowired
private Type1 myDependency1;
#Autowired
private Type2 myDependency2;
}
I want to test this controller so that "mydependency1" is mocked and everything else is autowired.
How can I do this?
I was previously following following test:
#Mock
private Type1 myDependency1;
#InjectMocks
private Mycontroller controller = new MyController();
private MockMvc mockMvc;
#Before
public void setUp(){
mockMvc = standaloneSetup(controller).build();
}
But this is only returning the controller with mock of myDependency1 and not injecting myDependency2.
Alright after playing around with different mock tools, I gave up on the mock part and went back to profiles Function of spring.
I created a new profile called mockXYZ in my application-context.xml
And created the service i wanted to mock, or give a certain response as
#Service("type1")
#Profile("mockXYZ")
public class Type1Mock implements Type1{
....
}
And when testing, I made mockXYZ as my active profile, and used autowired my controller.
Like this I was able to mock only one dependency while other dependency working as normal, as they have only one implementation and would be selected for any profile.
Hope this helps others as well.
Thank you

Resources