ReflectionTestUtils - 在目标对象上设置 [null] 类型的字段
ReflectionTestUtils - Setting field of type [null] on target object
我是单元测试和 Mockito 的新手。我正在尝试为我的 Dao 编写测试 class:
@Repository
@NoArgsConstructor
public class UserDaoImpl implements UserDao {
private NamedParameterJdbcTemplate template;
@Value("${users.find.by_id}")
private String findByIdQuery;
private RowMapper<User> rowMapper = (rs, rowNum) -> {
User user = new User();
user.setId(rs.getInt("id"));
user.setFirstName(rs.getString("firstname"));
user.setLastName(rs.getString("lastname"));
user.setEmail(rs.getString("email"));
user.setPassword(rs.getString("password"));
user.setEnabled(rs.getBoolean("enabled"));
return user;
};
public UserDaoImpl(NamedParameterJdbcTemplate template) {
super();
this.template = template;
}
@Override
public Optional<User> findById(int id) {
SqlParameterSource param = new MapSqlParameterSource("id", id);
User user = null;
try {
user = template.queryForObject(findByIdQuery, param, BeanPropertyRowMapper.newInstance(User.class));
} catch (DataAccessException ex) {
ex.printStackTrace();
}
return Optional.ofNullable(user);
}
}
在我的简单测试中,我只是为我的 NamedParameterJdbcTemplate 添加了 @Mock 注释,并尝试将其放入 UserDaoImpl:
public class UserDaoTest {
@Mock
public NamedParameterJdbcTemplate template;
@InjectMocks
public UserDao userDao;
@Test
public void findByIdTest() {
template = new NamedParameterJdbcTemplate(new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:db/schema.sql")
.addScript("classpath:db/test-data.sql")
.build());
userDao = new UserDaoImpl();
ReflectionTestUtils.setField(userDao, "template", template);
Mockito.when(userDao.findById(1).get().getEmail()).thenReturn("Keanu@gmail.com");
User user = userDao.findById(1).get();
assertNotNull(user);
assertEquals("Keanu@gmail.com", user.getEmail());
}
}
每次我 运行 测试时,我都会得到 java.lang.NullPointerException 字段 template。无法找到实施测试的正确方法。
这是我的 pom.xml:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>11</java.version>
</properties>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
您的代码有多个问题:
- 您直接在 属性 中使用
@Value
,您将很难用它来设置 class 的测试。
- 您缺少在测试中启用 Mockito 注释。
- 当您应该依赖 Mockito 创建的实例时,您正在测试方法中实例化
UserDaoImpl
。
- 您还创建了一个
NamedParameterJdbcTemplate
,然后使用 ReflectionTestUtils
将其连接到 UserDaoImpl
对象。
- 而且你在嘲笑错误的对象。您需要模拟对
template
的调用,而不是 userDao
.
要解决第一个问题,您需要按如下方式更改 UserDaoImpl
:
@Repository
@NoArgsConstructor
public class UserDaoImpl implements UserDao {
private NamedParameterJdbcTemplate template;
private String findByIdQuery;
private RowMapper<User> rowMapper = (rs, rowNum) -> {
User user = new User();
user.setId(rs.getInt("id"));
user.setFirstName(rs.getString("firstname"));
user.setLastName(rs.getString("lastname"));
user.setEmail(rs.getString("email"));
user.setPassword(rs.getString("password"));
user.setEnabled(rs.getBoolean("enabled"));
return user;
};
public UserDaoImpl(NamedParameterJdbcTemplate template, @Value("${users.find.by_id}") String findByIdQuery) {
super();
this.template = template;
this.findByIdQuery = findByIdQuery;
}
@Override
public Optional<User> findById(int id) {
SqlParameterSource param = new MapSqlParameterSource("id", id);
User user = null;
try {
user = template.queryForObject(findByIdQuery, param, BeanPropertyRowMapper.newInstance(User.class));
} catch (DataAccessException ex) {
ex.printStackTrace();
}
return Optional.ofNullable(user);
}
}
要解决 2.、3.、4. 和 5.,您需要以编程方式启用 Mockito 注释并从方法测试中删除 userDao = new UserDaoImpl();
行和 template
变量,如下所示:
@RunWith(MockitoJUnitRunner.class)
public class UserDaoTest {
@Mock
public NamedParameterJdbcTemplate template;
public UserDao userDao;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
userDao = new UserDaoImpl(template, "query-string");
}
@Test
public void findByIdTest() {
// Arrange
User user = new User();
user.setId(rs.getInt("id"));
user.setFirstName(rs.getString("firstname"));
user.setLastName(rs.getString("lastname"));
user.setEmail(rs.getString("Keanu@gmail.com"));
user.setPassword(rs.getString("password"));
user.setEnabled(rs.getBoolean("enabled"));
Mockito.when(template.queryForObject(anyString(), any(SqlParameterSource.class), any(RowMapper.class))).thenReturn(user);
template.queryForObject(findByIdQuery, param, BeanPropertyRowMapper.newInstance(User.class));
// Act
User user = userDao.findById(1).get();
// Assert
assertNotNull(user);
assertEquals("Keanu@gmail.com", user.getEmail());
}
}
我是单元测试和 Mockito 的新手。我正在尝试为我的 Dao 编写测试 class:
@Repository
@NoArgsConstructor
public class UserDaoImpl implements UserDao {
private NamedParameterJdbcTemplate template;
@Value("${users.find.by_id}")
private String findByIdQuery;
private RowMapper<User> rowMapper = (rs, rowNum) -> {
User user = new User();
user.setId(rs.getInt("id"));
user.setFirstName(rs.getString("firstname"));
user.setLastName(rs.getString("lastname"));
user.setEmail(rs.getString("email"));
user.setPassword(rs.getString("password"));
user.setEnabled(rs.getBoolean("enabled"));
return user;
};
public UserDaoImpl(NamedParameterJdbcTemplate template) {
super();
this.template = template;
}
@Override
public Optional<User> findById(int id) {
SqlParameterSource param = new MapSqlParameterSource("id", id);
User user = null;
try {
user = template.queryForObject(findByIdQuery, param, BeanPropertyRowMapper.newInstance(User.class));
} catch (DataAccessException ex) {
ex.printStackTrace();
}
return Optional.ofNullable(user);
}
}
在我的简单测试中,我只是为我的 NamedParameterJdbcTemplate 添加了 @Mock 注释,并尝试将其放入 UserDaoImpl:
public class UserDaoTest {
@Mock
public NamedParameterJdbcTemplate template;
@InjectMocks
public UserDao userDao;
@Test
public void findByIdTest() {
template = new NamedParameterJdbcTemplate(new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:db/schema.sql")
.addScript("classpath:db/test-data.sql")
.build());
userDao = new UserDaoImpl();
ReflectionTestUtils.setField(userDao, "template", template);
Mockito.when(userDao.findById(1).get().getEmail()).thenReturn("Keanu@gmail.com");
User user = userDao.findById(1).get();
assertNotNull(user);
assertEquals("Keanu@gmail.com", user.getEmail());
}
}
每次我 运行 测试时,我都会得到 java.lang.NullPointerException 字段 template。无法找到实施测试的正确方法。
这是我的 pom.xml:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>11</java.version>
</properties>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
您的代码有多个问题:
- 您直接在 属性 中使用
@Value
,您将很难用它来设置 class 的测试。 - 您缺少在测试中启用 Mockito 注释。
- 当您应该依赖 Mockito 创建的实例时,您正在测试方法中实例化
UserDaoImpl
。 - 您还创建了一个
NamedParameterJdbcTemplate
,然后使用ReflectionTestUtils
将其连接到UserDaoImpl
对象。 - 而且你在嘲笑错误的对象。您需要模拟对
template
的调用,而不是userDao
.
要解决第一个问题,您需要按如下方式更改 UserDaoImpl
:
@Repository
@NoArgsConstructor
public class UserDaoImpl implements UserDao {
private NamedParameterJdbcTemplate template;
private String findByIdQuery;
private RowMapper<User> rowMapper = (rs, rowNum) -> {
User user = new User();
user.setId(rs.getInt("id"));
user.setFirstName(rs.getString("firstname"));
user.setLastName(rs.getString("lastname"));
user.setEmail(rs.getString("email"));
user.setPassword(rs.getString("password"));
user.setEnabled(rs.getBoolean("enabled"));
return user;
};
public UserDaoImpl(NamedParameterJdbcTemplate template, @Value("${users.find.by_id}") String findByIdQuery) {
super();
this.template = template;
this.findByIdQuery = findByIdQuery;
}
@Override
public Optional<User> findById(int id) {
SqlParameterSource param = new MapSqlParameterSource("id", id);
User user = null;
try {
user = template.queryForObject(findByIdQuery, param, BeanPropertyRowMapper.newInstance(User.class));
} catch (DataAccessException ex) {
ex.printStackTrace();
}
return Optional.ofNullable(user);
}
}
要解决 2.、3.、4. 和 5.,您需要以编程方式启用 Mockito 注释并从方法测试中删除 userDao = new UserDaoImpl();
行和 template
变量,如下所示:
@RunWith(MockitoJUnitRunner.class)
public class UserDaoTest {
@Mock
public NamedParameterJdbcTemplate template;
public UserDao userDao;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
userDao = new UserDaoImpl(template, "query-string");
}
@Test
public void findByIdTest() {
// Arrange
User user = new User();
user.setId(rs.getInt("id"));
user.setFirstName(rs.getString("firstname"));
user.setLastName(rs.getString("lastname"));
user.setEmail(rs.getString("Keanu@gmail.com"));
user.setPassword(rs.getString("password"));
user.setEnabled(rs.getBoolean("enabled"));
Mockito.when(template.queryForObject(anyString(), any(SqlParameterSource.class), any(RowMapper.class))).thenReturn(user);
template.queryForObject(findByIdQuery, param, BeanPropertyRowMapper.newInstance(User.class));
// Act
User user = userDao.findById(1).get();
// Assert
assertNotNull(user);
assertEquals("Keanu@gmail.com", user.getEmail());
}
}