Mockito Test failed with when/thenReturn - java

I have written the controller test. However I am not able to pass the test. Is there anything wrong with the way I have written the test or the service method?
This is the test I'm running:
#Test
void controller_getUserTest() throws Exception {
UserEntity user = getUser(); //dummy user from getUser() helper method
when(userService.getUser("jeremy")).thenReturn(user);
this.mockMvc.perform(get("/user/jeremy")).andDo(print())
.andExpect(status().isOk());
}
This is the controller method that I'm testing:
#GetMapping("/{username}")
public ResponseEntity<UserEntity> getUser(#PathVariable String username) {
System.out.println(username);
UserEntity theUser = userService.getUser(username);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
if(theUser == null) {
String errorMessage = "User Not Found";
return new ResponseEntity(errorMessage,headers,HttpStatus.NOT_FOUND);
}
return new ResponseEntity<UserEntity>(theUser,headers,HttpStatus.OK);
}
This is the service method that was being mocked in when():
#Override
#Transactional
public UserEntity getUser(String username) {
UserEntity probe = new UserEntity();
probe.setUsername(username);
probe.setEnabled(true);
ExampleMatcher matcher = ExampleMatcher.matching().withIgnoreCase();
Example<UserEntity> example = Example.of(probe,matcher);
Optional<UserEntity> result = userRepository.findOne(example);
return result.isPresent()? result.get() : null;
The error message when the test failed:
org.mockito.exceptions.misusing.WrongTypeOfReturnValue:
UserEntity cannot be returned by findOne()
findOne() should return Optional
***
If you're unsure why you're getting above error read on.
Due to the nature of the syntax above problem might occur because:
1. This exception *might* occur in wrongly written multi-threaded tests.
Please refer to Mockito FAQ on limitations of concurrency testing.
2. A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies -
- with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() method.
I also have a getUser() method defined to init a dummy user for tests
Edit:
I realised that I wasnt using a mock instance of the UserService class, however, when I tried to use one, the error still appear.

I realised that the UserService instance I was using is an actual service instance instead of a mocked one, thats why it failed the test.

Related

Spring boot unit test not not working while returning hard coded values

I have below REST endpoint mapping.
#GetMapping("/employee/{id}")
public ResponseEntity<Employee> getEmployee(#PathVariable("id") int id) {
Employee employee = employeeRepository.getEmployeeById (id);
if(employee == null) {
throw new EmployeeNotFoundException ();
}
ResponseEntity<Employee> responseEntity = new ResponseEntity<Employee> (employee, HttpStatus.OK);
return responseEntity;
}
To test the failing path, I have the following test case.
#Test
public void getEmployeeFailTest() throws Exception {
Mockito.when (employeeRepository.getEmployeeById (Mockito.anyInt ())).thenReturn (null);
RequestBuilder requestBuilder = MockMvcRequestBuilders.get ("/employee/10")
.accept (MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform (requestBuilder).andReturn ();
String response = result.getResponse ().getContentAsString ();
System.out.println (employeeRepository.getEmployeeById (5)==null);
String expected = "{\"errorCode\":1,\"message\":\"404: Employee not found!\"}";
JSONAssert.assertEquals (expected, response, false);
Assert.assertEquals (404, result.getResponse ().getStatus ());
}
In the repository class, I am returning the hardcoded Employee object.
public Employee getEmployeeById(int i) {
Employee employeeMock = new Employee (1, "XYZ","randomEmail#gmail.com",new Department (1, "HR"));
return employeeMock;
}
The test cases are passing successfully when I return null in the above method. But with the above implementation, it fails.
Thanks to Mockito.when (employeeRepository.getEmployeeById (Mockito.anyInt ())).thenReturn (null); getEmployeeById is returning null in test method but in controller's method above hardcoded Employee object is getting returned
Am I missing something?
1) If I understand your test correctly then you expect "404 not found" in response to "employee/10". When you return null then REST controller throws EmployeeNotFoundException (which I assume handled via exception handler and converted to 404). When you return non-null object then exception is not thrown and test fails.
I suggest your repository class emulates object not found by
public Employee getEmployeeById(int i) {
return i==10 ? null : new Employee (1, "XYZ","randomEmail#gmail.com",new Department (1, "HR"));
}
2) Mockito.when (employeeRepository.getEmployeeById (Mockito.anyInt ())).thenReturn (null); this code does not seem working. I assume you do not inject employeeRepository to REST properly. You should mark it with #MockBean in your test class so Spring Test will prefer it over real implementation
Your employeeRepository instance in the REST controller is probably not the same instance you are trying to stub the return value of in your test.
A mock instance would normally return null by default for most reference types. Since you are getting the hardcoded object, it looks like your concrete implementation is being used in the REST controller.
Assuming your REST controller gets the employeeRepository through some kind of dependency injection, you need to make sure your mock is injected into it, either by explicitly injecting it or by providing a mock bean for your test's Spring Context.

How to make a async REST with Spring?

I'm trying to make a small REST using Spring Boot.
I've never used Spring and used Java a long time ago (Java 7)!
In the last 2 years I have used only Python and C# (but like I said, I already used Java).
So, now, I'm trying to make a REST using async methods, and checked several examples, but still, I don't understand very well the "correct way" to do this.
Looking at the following documentation: http://carlmartensen.com/completablefuture-deferredresult-async, Java 8 has CompletableFuture that I can use with Spring, so, I made the following code:
Service:
#Service
public class UserService {
private UserRepository userRepository;
// dependency injection
// don't need Autowire here
// https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-spring-beans-and-dependency-injection.html
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Async
public CompletableFuture<User> findByEmail(String email) throws InterrupedException {
User user = userRepository.findByEmail(email);
return CompletableFuture.completedFuture(user);
}
}
Repository:
public interface UserRepository extends MongoRepository<User, String> {
#Async
findByEmail(String email);
}
RestController:
#RestController
public class TestController {
private UserService userService;
public TestController(UserService userService) {
this.userService = userService;
}
#RequestMapping(value = "test")
public #ResponseBody CompletableFuture<User> test(#RequestParam(value = "email", required = true) String email) throws InterruptedException {
return userService.findByEmail(email).thenApplyAsync(user -> {
return user;
})
}
}
This code give me the expected output.
Then, looking at another documentation (sorry, I lost the link), I see that Spring accept the following code (which give me the expected output too):
#RequestMapping(value = "test")
public #ResponseBody CompletableFuture<User> test(#RequestParam(value = "email", required = true) String email) throws InterruptedException {
return userService.findByEmail(email);
}
}
Is there a difference between the two methods?
Then, looking at the following guide: https://spring.io/guides/gs/async-method/, there's a #EnableAsync annotation in SpringBootApplication class.
If I include the #EnableAsync annotation and create a asyncExecutor Bean like the code from last link, my application don't return nothing on /test endpoint (only a 200 OK response, but with blank body).
So, my rest is async without the #EnableAsync annotation?
And why when I use #EnableAsync, the response body is blank?
The response body is blank because the #Async annotation is used at findEmail method of UserRepository class, it means that there is no data returned to the following sentence User user = userRepository.findByEmail(email); because findByEmail method is running on other different thread and will return null instead of a List object.
The #Async annotation is enabled when you declare #EnableAsync that is the reason why it only happens when you use #EnableAsync because it activates the #Async of findEmail method to run it on other thread.
The method return userService.findByEmail(email); will return a CompletableFuture object that is created from UserService class.
The difference with the second method call is that thenApplyAsync method will create a totally new CompletableFuture from the previous one that comes from userService.findByEmail(email) and will only return the user object that comes from the first CompletableFuture.
return userService.findByEmail(email).thenApplyAsync(user -> {
return user;
})
If you want to get the expected results just remove the #Async annotation from findByEmail method, and finally add the #EnableAsync Annotation
If you need to clarify ideas of how to use Async methods, lets say that you have to call three methods and each one takes 2 seconds to finish, in a normal scenario you will call them method1, then method2 and finally method3 in that case you entire request will take 6 seconds. When you activate the Async approach then you can call three of them and just wait for 2 seconds instead of 6.
Add this long method to user service:
#Async
public CompletableFuture<Boolean> veryLongMethod() {
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
return CompletableFuture.completedFuture(true);
}
And call it three times from Controller, like this
#RequestMapping(value = "test")
public #ResponseBody CompletableFuture<User> test(#RequestParam(value = "email", required = true) String email) throws InterruptedException {
CompletableFuture<Boolean> boolean1= siteService.veryLongMethod();
CompletableFuture<Boolean> boolean2= siteService.veryLongMethod();
CompletableFuture<Boolean> boolean3= siteService.veryLongMethod();
CompletableFuture.allOf(boolean1,boolean2,boolean3).join();
return userService.findByEmail(email);
}
Finally measure the time that takes your response, if it takes more than 6 seconds then you are not running Async method, if it takes only 2 seconds then you succeed.
Also see the following documentation: #Async Annotation, Spring async methods, CompletableFuture class
Hope it help.
The Asynchronous child threads start executing very late (around 20 to 30 seconds delay).
I'm using ThreadPoolTaskExecutor() in my main SpringBoot application class. You can also try the same if you consider performance as a factor.

spring mvc how to test that my service persists entities in post request

So I'm writing this web app with Spring Boot using Spring Data with JPA and Spring MVC and I would like to make mock controller tests. I figured out how to test the get method, but in my controllers post method a new JPA entity is being either persisted or updated with my service. Here is what my controller looks like:
#Controller
#RequestMapping("/registerMember")
public class RegisterMemberController {
#Autowired
private MemberService memberService;
#GetMapping
public String index(RegisterMemberBean registerMemberBean) {
return "registerMember";
}
#PostMapping
public String handleSubmit(#Valid RegisterMemberBean registerMemberBean, BindingResult bindingResult, Model model) {
Member member = registerMemberBean.getMember();
boolean isRepeatPasswordCorrect = !isRepeatPasswordIncorrect(member.getPassword(), registerMemberBean.getComparePassword());
if(isAnyErrors(isRepeatPasswordCorrect, !bindingResult.hasErrors())) {
if(!isRepeatPasswordCorrect) {
model.addAttribute("isRepeatPasswordIncorrect", true).
addAttribute("isRepeatPasswordIncorrectMsg", "Passwords don't match");
}
return "registerMember";
}
boolean errUsername = !memberService.isNoOtherEntityWithUserName(0, member.getUserName());
boolean errEmail = !memberService.isNoOtherEntityWithEmail(0, member.getEmail());
if(errUsername || errEmail) {
if(errUsername) {
model.addAttribute("isExistingUserName", true).addAttribute("isExistingUserNameMsg", "Already a user with that username");
} if(errEmail) {
model.addAttribute("isExistingEmail", true).addAttribute("isExistingEmailMsg", "Already a user with that email");
}
return "registerMember";
}
getMainService().save(member);
return redirectTo("index", new RedirectEntity("member", member.getId()));
}
}
Now in my mock controller test i want to make make sure that my post method does the following:
Reload the page if the BindingResults has any errors
My service persists the member JPA entity in db (if no errors)
Method redirects me to the index page
This is what my (poor) test class looks like so far:
#RunWith(SpringRunner.class)
#TestPropertySource(locations="classpath:application_test.properties")
#WebAppConfiguration
public class RegisterMemberControllerTest {
private MockMvc mockMvc;
#MockBean
private MemberService memberService;
#MockBean
private RegisterMemberController controller;
#Before
public void init() {
mockMvc = MockMvcBuilders.standaloneSetup(controller).setViewResolvers(new StandaloneMvcTestViewResolver()).build();
controller.setMainService(memberService);
}
#Test
public void testIndex() throws Exception {
mockMvc.perform(get("/registerMember"))
.andExpect(status().isOk())
.andExpect(forwardedUrl("registerMember");
}
#Test
public void testHandleSubmit() throws Exception {
RegisterMemberBean registerMemberBean = new RegisterMemberBean();
registerMemberBean.setMember(TestFixture.getValidMemberWithoutReferences());
Member member = TestFixture.getValidMember();
mockMvc.perform(post(Page.REGISTER_MEMBER)).andExpect(status().isOk());
when(mockMvc.perform(post(Page.REGISTER_MEMBER)).andExpect((ResultMatcher) memberService.save(member)).andExpect(forwardedUrl("redirect:/index/member=" + member.getId() + ".html")));
}
}
to my understanding spring boot uses Mockito. I have some experience with EasyMock but I would like to use the spring defaults as much as possible. Can someone show how to achieve this?
I think there is a little bit of confusion on what should and shouldn't be mocked.
If I read your question correctly, you are actually trying to Unit Test your RegisterMemberController. Therefore, you most likely should NOT make a mock of that class, but actually test that class.
I believe that you would be creating fakes/dummies/stubs/mocks/spies of your MemberService, RegisterMemberBean, and BindingResult classes.
It would be these classes that would be created by your unit test and handed to your controller during the test that will force the testing of the logic that you are interested in proving/disproving.
FYI, when verifying that the MemberService class was called, that is where you would use a mock. The rest of the classes could either be dummies or stubs.
Side Note: I would recommend removing the Model parameter from your handleSubmit() method since it doesn't seem to be used anywhere.

How to test than an IllegalArgumentException is thrown in a RESTful controller using MockRestServiceServer

I have a very simple RESTful controller that produces a json and I want to test if it works ok.
I can easily test the method when the user id exists, no problem with that.
I would like to validate that an IllegalArgumentException is thrown when the given id does not exist. Is there any way to do that?
In the application, when an assertion error occurs, the response is redirected to a generic error page that displays a generic message.
If I use response(withStatus(HttpStatus.NOT_FOUND) I'm not validating the exception nor my message.
Can somebody help me please?
This is the method I want to test:
#Transactional (readOnly = true)
#RequestMapping(method=RequestMethod.GET, value="/json/user/{id}", headers="Accept=application/json, text/html")
public #ResponseBody UserData getUserJson(#PathVariable Long id)
{
User user = this.userService.findUserById(id);
Assert.notNull(user , "User does not exist with the given id: " + id);
return new UserData (user);
}
This is a test with a valid id:
#Test
public void testRestUserJson() {
mockServer.expect(requestTo(JSON_URL + USER)).andExpect(method(HttpMethod.GET))
.andRespond(withSuccess(JSON_RESPONSE, MediaType.APPLICATION_JSON));
UserData userData = restClientTemplate.getForObject(JSON_URL + USER, UserData.class);
AssertResponseContent(userData , expectedData);
mockServer.verify();
EasyMock.verify(user);
}
Regards,
Daniela
Why don't you catch the exceptions and create a log and write the exception to log.
Update your test as below.
#Test(expected = IllegalArgumentException.class)
public void testRestUserJson() { ... }
Note the additional parameter expected which specify the Exception which must be thrown while test execution.
Additionally to assert the exception message, error code etc. create a custom exception matcher. Refer my answer for details and pseudo code.

Mockito throwing a NullpointerException on using a mock

I'm trying to create test cases for a webservice but I'm getting nullpointerexception. This is the web service:
#Path("friendservice")
public class FriendWebService {
private static final Logger logger = Logger.getLogger(FriendWebService.class);
#EJB
private FriendRequestServiceInterface friendRequestService;
#GET
#Path("friendrequest")
#Produces(MediaType.TEXT_PLAIN)
public String createFriendRequest(
#Context HttpServletRequest request) {
logger.info("createFriendRequest called");
String result = "false";
User user = (User) request.getSession().getAttribute("user");
User otherUser = (User) request.getSession().getAttribute("profileuser");
if ((user != null) && (otherUser != null)) {
logger.info("Got two users from session, creating friend request.");
if (friendRequestService.createFriendRequest(user, otherUser)) {
result = "true";
}
}
return result;
}
}
This is my test class:
public class FriendWebServiceTest {
#Mock
FriendRequestServiceInterface FriendRequestService;
#Mock
Logger mockedLogger = mock(Logger.class);
#Mock
HttpServletRequest mockedRequest = mock(HttpServletRequest.class);
#Mock
HttpSession mockedSession = mock(HttpSession.class);
#Mock
User mockedUser = mock(User.class);
#Mock
User mockedOtherUser = mock(User.class);
#InjectMocks
FriendWebService friendWebService = new FriendWebService();
#Before
public void setUp() throws Exception {
}
#Test
public void testCreateFriendRequest() throws Exception {
when(mockedRequest.getSession()).thenReturn(mockedSession);
when(mockedSession.getAttribute("user")).thenReturn(mockedUser);
when(mockedSession.getAttribute("profileuser")).thenReturn(mockedOtherUser);
when(FriendRequestService.createFriendRequest(mockedUser, mockedOtherUser)).thenReturn(true);
assertTrue(friendWebService.createFriendRequest(mockedRequest) == "true");
}
The NullPointerException occurs at "when(FriendRequestService.createFriendRequest(mockedUser, mockedOtherUser)).thenReturn(true);"
What am I doing wrong?
You are chaining method calls on your mocked instance:
#Mock
HttpServletRequest mockedRequest = mock(HttpServletRequest.class);
First of all, you do not need to do both, either use the #Mock annotation or the mock method. Like this, you first assign a Mock and then replace this instance with another mock. I recommend the annotation as it adds some context to the mock such as the field's name. This might already cause your NullPointerException as you however never activate the annotations by calling:
MockitoAnnotations.initMocks(this);
as you do not consequently mock all instances with both measures so far. However, even doing so will further result in your exception, so let's move ahead.
Within friendWebService.createFriendRequest(mockedRequest) you call:
User user = (User) request.getSession().getAttribute("user");
User otherUser = (User) request.getSession().getAttribute("profileuser");
where you call a method on two mocks for which you did not specify any behavior. These mocks do by default return null. You need to specify behavior for this such as:
when(request.getSession()).thenReturn(myMockedSession);
before performing this chained call. Based on this, you can then specify how to react to calls on this mocked instance such as returning your user mocks.
Instead of calling initMocks, You probably need to annotate with #RunWith(MockitoJUnitRunner.class) to your FriendWebServiceTest class.
You can also try adding #RunWith(MockitoJUnitRunner.class) or #ExtendWith(MockitoExtension.class) annotations to your FriendWebServiceTest class for JUnit4 and JUnit5 respectively.

Categories