What is sense of #Mock annotation? - spring

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.

Related

Mockito #Spy calls real method while mocking behaviour is defined

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.

Mockito test coming back as zero

I am trying to get call method called totalmoney() to get the total money in the h2 database but it always returns 0.
#RunWith(MockitoJUnitRunner.class)
public class MoneyTests {
#InjectMocks
MoneyServiceImplementation MoneyServiceImplementation;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
}
#Test
public void getAllMoney() {
long total_money = MoneyServiceImplementation.TotalMoney();
assertEquals("2000", total_money);
}}
But it will return the right amount of 2000 by:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("Bean.xml");
MoneyService MoneyService = (MoneyService) context.getBean("MoneyServiceImplementation");
long total_money = MoneyService.TotalMoney();
So what am i doing wrong in the test that it will not work?
Mockito is not an dependency injection framework, don't expect this shorthand utility to inject a complex graph of objectsbe it mocks/spies or real objects.
Again, note that #InjectMocks will only inject mocks/spies created using the #Spy or #Mock annotation.
There are no any #Mock or #Spy annotated beans in your test, so all of the dependencies in the created MoneyServiceImplementation are null.

mock beans inside a method and those beans are also autowired in the same class or super class

#RunWith(SpringRunner.class)
#WebAppConfiguration
Class Test{
#Autowired
public SomeBean someBean;
#Test
public void testAccountPage2() throws Exception {
SomeBean someBean = mock(SomeBean.class);
given(someBean.getAccount(anyString())).willReturn(getCustomer());
}
Here someBean.getAccount(anyString()) is not mocking, which is calling the actual method of that bean impl. It seems it's taking the Autowired object not the mocked one.
Could anyone help me to mock beans in method level? those beans are also autowired in the same class or superclass.
Thanks
To replace a bean in the Spring container by a Mockito mock, use #MockBean.
import org.springframework.boot.test.mock.mockito.MockBean; // import to add
#RunWith(SpringRunner.class)
#WebAppConfiguration
Class Test{
#MockBean
public SomeBean someBean;
#Test
public void testAccountPage2() throws Exception {
given(someBean.getAccount(anyString())).willReturn(getCustomer());
}
}
To understand the difference between Mockito and MockBean from Spring Boot, you can refer to this question.
You need to inject the mock in order to have it working instead of autowiring
//if you are just testing bean/services normally you do not need the whole application context
#RunWith(MockitoJUnitRunner.class)
public class UnitTestExample {
#InjectMocks
private SomeBean someBean = new SomeBean();
#Test
public void sampleTest() throws Exception {
//GIVEN
given(
someBean.getAccount(
//you should add the proper expected parameter
any()
)).willReturn(
//you should add the proper answer, let's assume it is an Account class
new Customer()
);
//DO
//TODO invoke the service/method that use the getAccount of SomeBean
Account result = someBean.getAccount("");
//VERIFY
assertThat(result).isNotNull();
//...add your meaningful checks
}
}

Mock Spring Data Rest Repository

I faced with problem while testing data rest repositories. I call rest resource and check if it gets me proper json. But for pre-population data I don't want to use in memory db, so I mocked repository method invocation.
#MockBean
private CommentRepository commentRepository;
and did this
given(commentRepository.findOne(1L)).willReturn(comment);
And now, while calling "/comments/1" I get 404 error, so data rest didn't expose my repository. The main question is "How can we mock repository method for getting data from database?"
My test class:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class CommentTest
{
#Autowired
private TestRestTemplate restTemplate;
#MockBean
private CommentRepository commentRepository;
#Before
public void setup()
{
Comment comment = new Comment();
comment.setText("description");
comment.setCommentId(1L);
given(commentRepository.findOne(1L)).willReturn(comment);
}
#Test
public void shouldCheckCommentGetResource()
{
ParameterizedTypeReference<Resource<Comment>> responseType = new ParameterizedTypeReference<Resource<Comment>>() {};
ResponseEntity<Resource<Comment>> responseEntity =
restTemplate.exchange("/comments/1", HttpMethod.GET, null, responseType, Collections
.emptyMap());
Comment actualResult = responseEntity.getBody().getContent();
assertEquals("description", actualResult.getText());
// more assertions
}
}
As I understand, using MockBean annotation I replace current repository bean and it will not be exposed by data rest, Do we have any way to pre-populate data into db or mock invocation of repository method?
I do not think this is possible. Spring data registers the repository beans using a FactoryBean - in the spring-data-rest case this is EntityRepositoryFactoryBean. So you cannot just override these beans with a mock.
For an interesting read on why mocking spring data repositories is not useful see this answer https://stackoverflow.com/a/23442457/5371736
In the same thread there is a reference to a project introducing mock support for spring-data repositories - https://stackoverflow.com/a/28643025/5371736
There is quick and dirty way to mock Spring Data Rest repositories with mockito, just in case somebody have no other options, but try to avoid this unless absolutely necessary
class MockRestRepositoryUtil {
public static <T> T mockRepository(Class<T> repositoryClass,
T springRepository) throws Exception {
Object springRepositoryImpl = AopTestUtils.getTargetObject(springRepository);
T mockRepository = mock(repositoryClass, delegatesTo(springRepositoryImpl));
Object springProxyHandler = Proxy.getInvocationHandler(springRepository);
ProxyFactory proxyFactory = extractProxyFactory(springProxyHandler);
proxyFactory.setTarget(mockRepository);
removeSpringDataAspects(proxyFactory);
return mockRepository;
}
private static ProxyFactory extractProxyFactory(Object springProxyHandler) throws Exception {
Field advisedField = springProxyHandler.getClass().getDeclaredField("advised");
advisedField.setAccessible(true);
return (ProxyFactory) advisedField.get(springProxyHandler);
}
private static void removeSpringDataAspects(ProxyFactory proxyFactory) {
Advisor[] advisors = proxyFactory.getAdvisors();
Arrays.stream(advisors)
.filter(advisor -> advisor.getAdvice().getClass().getPackage().getName()
.contains("org.springframework.data"))
.collect(toImmutableList())
.forEach(proxyFactory::removeAdvisor);
}
}

In my JUnit test, how do I verify a Spring RedirectView?

I'm using Spring 3.2.11.RELEASE and JUnit 4.11. In a particular Spring controller, I have a method that ends thusly ...
return new ModelAndView(new RedirectView(redirectUri, true));
In my JUnit test, how do I verify return from a submission to my controller in which this RedirectView is returned? I used to use org.springframework.test.web.AbstractModelAndViewTests.assertViewName, but that only returns "null", even when a non-empty ModelAndView object is returned. Here is how I'm constructing my JUnit test ...
request.setRequestURI(“/mypage/launch");
request.setMethod("POST");
…
final Object handler = handlerMapping.getHandler(request).getHandler();
final ModelAndView mav = handlerAdapter.handle(request, response, handler);
assertViewName(mav, "redirect:/landing");
Any help on how to verify that a RedirectView comes back with the proper value is appreciatd,
As Koiter said, consider moving to spring-test a and MockMvc
It providers some methods to test controllers and requests/reponses in a declarative way
you will need a #Autowired WebApplicationContext wac;
and on your #Before method setup this to use the #WebAppConfiguration of the class.
You'll end up with something
#ContextConfiguration("youconfighere.xml")
//or (classes = {YourClassConfig.class}
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
public class MyControllerTests {
#Autowired WebApplicationContext wac
private MockMvc mockMvc;
#Before
public void setup() {
//setup the mock to use the web context
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
}
Then you just need to use the MockMvcResultMatchers to assert things
#Test
public void testMyRedirect() throws Exception {
mockMvc.perform(post("you/url/")
.andExpect(status().isOk())
.andExpect(redirectUrl("you/redirect")
}
Note: post(), status() isOk() redirectUrl() are statics imports from MockMvcResultMatchers
See more what you can match here
Considering change your tool to MockMvc.
First you should create your MockMvc based on your controller.
private MockMvc mockController;
mockController =
MockMvcBuilders.standaloneSetup(loginController).setCustomArgumentResolvers(
new ServletWebArgumentResolverAdapter(new PageableArgumentResolver())).build();
After you create that object build the request with the request information. Part of this is the assert options that are contained in the API.
mockController.perform(MockMvcRequestBuilders.get(LoginControllerTest.LOGIN_CONTROLLER_URL + "?logout=true").
principal(SessionProvider.getPrincipal("GonLu004")))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.view().name("jsp/login"))
.andExpect(MockMvcResultMatchers.model().attribute("logOutMessage", logoutMessage));
The MockMvcResultMatchers contains a method for reviewing redirect information.
MockMvc from spring is a good choice to apply your unit testing on the controller layer.

Resources