Matt Chermside
Matt Chermside

Reputation: 1

Mockito When...Then Return - return ignored?

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!

Upvotes: 0

Views: 5179

Answers (4)

Marko
Marko

Reputation: 179

I had a similar problem. But in my case nothing helped. I used something like

when(userRepository.someMethod(any(), any(), any())).thenReturn(Optional.of(something));

but it always returns empty optional no matter what I did. Then I simply changed the way how the testing service is created. I did not use @InjectMocks annotation but rather created my service manually using constructor to provide mocks. After that it worked correctly. I don't know what is the reason of that because I have used the same mock objects I have used with @InjectMocks annotation. I already had bad experience with that annotation so I will not use it anymore. This annotation also not working correctly with functional interfaces. Be careful when using this annotation.

Upvotes: 0

JRichardsz
JRichardsz

Reputation: 16505

My case was not the same of your findById but I hope it helps.

Summary:

mockito is sensible with the passed arguments and it works better with simple or primitive data types

On my case, this sentence was ignored:

doReturn(result).when(scriptExecutor).
runScript(connection, new StringReader("selec * from dual;"));

After some hours of attempts, I change the definition of runScript to receive a simple string instead StringReader.

From this:

public ArrayList runScript(Connection conn, StringReader reader)

To this:

public ArrayList runScript(Connection conn, String query)

Then the mocked sentence was picked and everything works as expected:

doReturn(result).when(scriptExecutor).
runScript(connection, "selec * from dual;");

Basically if I use a StringReader, didn,t work. But with a simple String, it works!!

Upvotes: 0

beer geek
beer geek

Reputation: 371

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));

Upvotes: 0

Janar
Janar

Reputation: 2701

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

Upvotes: 0

Related Questions