D
D
Denis Kuznetsov2021-03-07 15:55:51
Java
Denis Kuznetsov, 2021-03-07 15:55:51

Why Mock gives incorrect data?

Hello, I have a test class

@RunWith(MockitoJUnitRunner.class)
@SpringBootTest
public class UserRepositoryImplTest {

    private static final Logger log = LoggerFactory.getLogger(UserRepositoryImplTest.class);

    @Mock
    JdbcTemplate jdbcTemplate;

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

    @Test
    public void findByNameTest() {
        User user = setupUser();
        String userName = user.getUserName();
        UserRepository userRepository = new UserRepositoryImpl();
        ReflectionTestUtils.setField(userRepository, "jdbcTemplate", jdbcTemplate);
        Mockito.when(jdbcTemplate.queryForObject(
                FIND_BY_NAME,
                new ForUnitTestUserRowMapper(),
                userName))
                .thenReturn(user);
        assertEquals(user, userRepository.findByName(userName));
    }

}

I did it according to the following example: Example .
But I get NPE when trying to compare what the mock returns and the prepared result.
Here is the error stacktrace and logs before it
16:23:10.048 [main] DEBUG org.springframework.test.util.ReflectionTestUtils - Setting field 'jdbcTemplate' of type [null] on target object [com[email protected]7d61eccf ] or target class [class com.trainig.spring.main.project.repository.user.UserRepositoryImpl] to value [jdbcTemplate]
16:23:10.101 [main] INFO com.trainig.spring.main.project.repository.user. UserRepositoryImpl - find by name : name
16:23:10.121 [main] INFO com.trainig.spring.main.project.repository.user.UserRepositoryImpl - Empty user

java.lang.NullPointerException
at com.trainig.spring.main.project. entity.User.equals(User.java:136)
com.training.spring.main.project.repository.user.UserRepositoryImplTest.findByNameTest(UserRepositoryImplTest.java:50)
...
[MockitoHint] UserRepositoryImplTest.findByNameTest (see javadoc for MockitoHint):
[MockitoHint] 1. Unused... -> at com.training.spring.main.project.repository.user.UserRepositoryImplTest.findByNameTest(UserRepositoryImplTest.java: 45)
[MockitoHint] ...args ok? -> at com.trainig.spring.main.project.repository.user.UserRepositoryImpl.findByName(UserRepositoryImpl.java:63)


here is the method to prepare the object for testing:
public static User setupUser() {
        User user = new User();
        user.setUserId(ID);
        user.setUserName(USER_NAME);
        user.setUserPassword(PASSWORD);
        user.setRoles(Collections.singleton(new Role(ID, USER_ROLE)));
        return user;
    }


Here is the method I am testing:
@Override
    public User findByName(String userName) {
        logger.info("find by name : {}", userName);
        User user = jdbcTemplate.queryForObject(
                FIND_BY_NAME,
                (rs, rowNum) -> new User(
                        rs.getLong("user_id"),
                        rs.getString("user_name"),
                        rs.getString("user_password"),
                        Collections.singleton(new Role(
                                rs.getLong("role_id"),
                                rs.getString("role_name")
                        ))),
                userName);
        if (Objects.isNull(user)) {
            logger.info("Empty user");
            return new User();
        } else {
            return user;
        }
    }

Here is the query I am using
public static final String FIND_BY_NAME = "select ut.user_id, ut.user_name, ut.user_password," +
            " user_role.role_id, role_name from \n" +
            "user_role inner join user_to_role\n" +
            "on user_role.role_id = user_to_role.role_id \n" +
            "inner join user_table as ut\n" +
            "on ut.user_id = user_to_role.user_id\n" +
            "where ut.user_name = ?";


The test method works if in the repository at the time of checking for zero, return not a new user, but a set user. From which I conclude that the result of the work of the mock is the result of the work of the method itself, and not what I wrote in
.thenReturn(user)

That is, if in the UserRepositoryImpl
class change from
if (Objects.isNull(user)) {
            logger.info("Empty user");
            return new User();
        } else {
            return user;
        }


on the
if (Objects.isNull(user)) {
            logger.info("Empty user");
            return ModelUtil.setupUser();
        } else {
            return user;
        }

then the test works correctly. What should I do to make it work correctly
Mockito.when(jdbcTemplate.queryForObject(
                FIND_BY_NAME,
                new ForUnitTestUserRowMapper(),
                userName))
               <b> .thenReturn(user);</b>

?

Answer the question

In order to leave comments, you need to log in

1 answer(s)
D
Dmitry Roo, 2021-03-07
@xez

1. The most important thing. There is no point in integration tests to mock the database. To do this, use better testcontainers (at worst h2, but this is a sure way to shoot yourself in the foot).
2.

ReflectionTestUtils.setField(userRepository, "jdbcTemplate", jdbcTemplate);
wtf? Is there really no other way?
3. Mock should use "matchers" eq, any, etc.
Try something like this:
Mockito.when(jdbcTemplate.queryForObject(
                eq(FIND_BY_NAME),
                any(ForUnitTestUserRowMapper.class),
                anyString())
               .thenReturn(user);

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question