Mock object is null in JUnit Test

1k Views Asked by At

I design the test in JUnit using Mockito. The problem is in the following Test:

@ExtendWith(MockitoExtension.class)
class UserServiceTest {
    
    @InjectMocks
    private UserService userService;
    @Mock
    private UserDAO userDAO;
    @Mock
    private ModelMapper modelMapper;
    @Mock
    private UserDTOMapper userDTOMapper;

    private User user;
    private UserDTO userDTO;

    private AutoCloseable closeable;

    @BeforeTestMethod
    public void initMocks(){
        MockitoAnnotations.openMocks(this);
    }

    @BeforeEach
    void setUp() {
        userDTO = UserDTO.builder()
                .username("johnny")
                .firstname("John")
                .surname("Rogers")
                .email("[email protected]")
                .phoneNumber("0123456789")
                .role(Role.ADMIN).build();
        user = User.builder()
                .username("johnny")
                .password("johnny123")
                .firstname("John")
                .surname("Rogers")
                .email("[email protected]")
                .phoneNumber("0123456789")
                .role(Role.ADMIN).build();
    }

    @Test
    void findUser() {
        userDao.sace(user);
        assertEquals(userDTO, userService.findUser(user.getUsername()));
    }

    @Test
    void convertToDto() {
        assertEquals(userDTO, userDTOMapper.apply(user));
    }


    @AfterTestMethod
    public void releaseMocks() throws Exception {
        closeable.close();
    }
}

The test is for this service class:

@Service
@RequiredArgsConstructor
public class UserService {

    private final UserDAO userDAO;
    private final ModelMapper modelMapper;
    private final UserDTOMapper userDTOMapper;

    public UserDTO findUser(String username) {
        User user = userDAO.findUserByUsername(username);
        return userDTOMapper.apply(user);
    }

    public UserDTO convertToDto(User user) {
        return modelMapper.map(user, UserDTO.class);
    }
}

Error:

org.opentest4j.AssertionFailedError: 
Expected :UserDTO(id=0, username=johnny, firstname=John, surname=Rogers, [email protected], phoneNumber=0123456789, role=USER)
Actual   :null
<Click to see difference>


    at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:151)
    at org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:132)
    at org.junit.jupiter.api.AssertEquals.failNotEqual(AssertEquals.java:197)
    at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:182)
    at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:177)
    at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:1142)

I'm not really sure what's the problem or am I doing something wrong? At first, I thought that the problem is in "@RequiredArgsConstructor" annotation, but that's not a problem. I'm sure that the solution is something simple, but I don't really understand why all the mocked objects are NULL

2

There are 2 best solutions below

4
Feel free On BEST ANSWER
  1. You don't need to use openMocks(). The annotation @ExtendWith(MockitoExtension.class) already did it for you.
  2. You didn't stub any of the behavior for your dependencies.
  3. Remove after and before methods. if you want to create Instances for your User and Dto just use private methods. You do not need to create it after every test.

Here is my example of how it should look. If it worked then you can start refactoring:

@ExtendWith(MockitoExtension.class)
public class UserServiceTest {

    @Mock
    private UserDAO userDAO;

    @Mock
    private ModelMapper modelMapper;

    @InjectMocks
    private UserService userService;

    @Test
    public void testFindUser() {
        // Arrange
        String username = "testUser";
        User user = User.builder()
                .id(1)
                .username(username)
                .firstname("John")
                .surname("Doe")
                .email("[email protected]")
                .phoneNumber("123456789")
                .role(Role.USER)
                .build();
        when(userDAO.findUserByUsername(username)).thenReturn(user);

        UserDTO expectedDto = UserDTO.builder()
                .id(1)
                .username(username)
                .firstname("John")
                .surname("Doe")
                .email("[email protected]")
                .phoneNumber("123456789")
                .role(Role.USER)
                .build();
        when(userDTOMapper.apply(user)).thenReturn(expectedDto);
        
        UserDTO result = userService.findUser(username);
        
        assertEquals(expectedDto, result);
    }

    @Test
    public void testConvertToDto() {
        // Arrange
        User user = User.builder()
                .id(1)
                .username("testUser")
                .firstname("John")
                .surname("Doe")
                .email("[email protected]")
                .phoneNumber("123456789")
                .role(Role.USER)
                .build();

        UserDTO expectedDto = UserDTO.builder()
                .id(1)
                .username("testUser")
                .firstname("John")
                .surname("Doe")
                .email("[email protected]")
                .phoneNumber("123456789")
                .role(Role.USER)
                .build();

        when(modelMapper.map(user, UserDTO.class)).thenReturn(expectedDto);
        
        UserDTO result = userService.convertToDto(user);

        assertEquals(expectedDto, result);
    }

}

1
Sendi On

I can see you have an answer to your question already.

But here is what "mocking" and "stubbing" mean in simple words. By mocking your object, you tell the compiler to create a nullified version of the real instance of that object. So while using the methods of that instance, you might get a NPE or the method might return the default value of the return data type. Hence, stubbing comes in handy in this case. By using when() method of Mockito, you will teach Mockito how to act upon method calls, whether it should throw an exception, or it should call the real method, or it should do nothing.

Think of JUnit and Mockito as a 4 year-old kid. When you assign a task to that kid, he might not be able to operate on his own, but he might need some instructions to do something.