0
votes

I'm currently on a course learning Spring-boot and I'm stuck with testing a project - any help is much appreciated as I'm a beginner here.

I have a rest controller test, using Mockito that appears to be ignoring "ThenReturn" when a method is invoked using Mockito.when().

Here is the whole class:

package com.example.demo.controllers;

import com.example.demo.TestUtils;
import com.example.demo.model.persistence.AppUser;
import com.example.demo.model.persistence.repositories.CartRepository;
import com.example.demo.model.persistence.repositories.UserRepository;
import com.example.demo.model.requests.CreateUserRequest;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import java.util.Optional;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

public class UserControllerTest {

    private UserController userController;
    private UserRepository userRepository = mock(UserRepository.class);
    private CartRepository cartRepository = mock(CartRepository.class);
    private BCryptPasswordEncoder bCryptPasswordEncoder = mock(BCryptPasswordEncoder.class);

    @Before
    public void initTest(){
        userController = new UserController();
        TestUtils.injectObjects(userController, "userRepository", userRepository);
        TestUtils.injectObjects(userController, "cartRepository", cartRepository);
        TestUtils.injectObjects(userController, "bCryptPasswordEncoder", bCryptPasswordEncoder);
        AppUser appUser = TestUtils.getAppUser();
        when(userRepository.findById(0L)).thenReturn(Optional.of(appUser));
        when(bCryptPasswordEncoder.encode("testPassword")).thenReturn("hashedPassword");
    }

    @Test
    public void testFindUserById(){
        ResponseEntity<AppUser> response = userController.findById(0L);
        System.out.println(response);
    }


    @Test
    public void testCreateUser() throws Exception{

        CreateUserRequest createUserRequest = new CreateUserRequest();
        createUserRequest.setUsername("testUser");
        createUserRequest.setPassword("testPassword");
        createUserRequest.setConfirmPassword("testPassword");

        ResponseEntity<AppUser> response = userController.createUser(createUserRequest);

        assertNotNull(response);
        assertEquals(200, response.getStatusCodeValue());

        AppUser createdUser = response.getBody();
        assertNotNull(createdUser);
        assertEquals(0, createdUser.getId());
        assertEquals("testUser", createdUser.getUsername());
        assertEquals("hashedPassword", createdUser.getPassword());
    }
}

The test called "testCreateUser" passes without a problem. It's the test called "testFindUserById" that is giving me a problem.

Here is the controller method I'm trying to test (all working fine when tested in Postman):

public ResponseEntity<AppUser> findById(@PathVariable Long id) {
        try{
            log.info("UserIDSearch = " + id);
            System.out.println("UserIDSearch = " + id);
            Optional<AppUser> optionalAppUser = userRepository.findById(id);
            if(optionalAppUser.isPresent()){
                log.info("UserIdFound =  " + id);
                return ResponseEntity.ok(optionalAppUser.get());
            }else{
                throw new ApiException(ExceptionTypes.SEARCHUSER, id.toString());
            }
        }catch(ApiException a){
                return ResponseEntity.notFound().build();
        }
    }

The repository being mocked in the test class is just a straightforward JpaRepository:

public interface UserRepository extends JpaRepository<AppUser, Long> {
    Optional<AppUser> findByUsername(String username);
    public Optional<AppUser> findById(long id);
}

The output I get from running the testFindUserById() test is the following:

UserIDSearch = 0
<404 NOT_FOUND Not Found,[]>

I guess what I'm trying to achieve here is that the test uses the when().thenReturn() to simulate an OK response from the mocked userRepository, but instead it actually performs the search and returns the "Not found". Can anyone help? Thanks so much!

2
what's the thrown exception message? - Janar
Thanks for responding! There is no exception - it just seems to completely ignore the thenReturn(Optional.of(appUser)) part and actually performs the search on the repository - I have no idea why. A bit like when the testCreateUser() method simulates returning a password from the bCryptPasswordEncoder, I'm trying to get the testFindUserById method to simulate returning a user from the repository. - Matt Chermside
from the response it seems that the code goes to the catch block - is that not the case? - Janar
As I understand it (probably incorrectly) the search shouldn't ever actually be performed as mockito should pick up the call Optional<AppUser> optionalAppUser = userRepository.findById(id); and just return Optional.of(appUser) which can be used to build the ReponseEntity returned by the controller method.But that's not happening. - Matt Chermside
Well, you're a legend. Your two small hints have enabled me to get 3 out of 4 of my test classes working completely. Thank you very much! - Matt Chermside

2 Answers

0
votes

Seems to be a problem with autoboxing. Change the method public Optional<AppUser> findById(long id); to accept Long instead.

0
votes

I think @Janar is correct, but I'll explain why.

When you write "when(userRepository.findById(0L)).thenReturn(Optional.of(appUser))", what Mockito does is set start monitoring calls to the indicated method (findById) and comparing the argument to "0L", which is a Java long int, which is not an object.

The actual method takes a java.lang.Long, which is an object. The two are not equal.

You can fix it by changing the findById method, as @Janar suggests, but that is not what I would do. I assume that the production code is using a java.lang.Long for a practical reason. Instead, I would change the test to:

when(userRepository.findById(Long.valueOf(0L))).thenReturn(Optional.of(appUser));