使用 Mockito Mock 方法式,一直以为可以用 anyString()
, any(Foo.class)
等匹配 null
值,其实不行,null
值必须显式的用 null
, 或 eq(null)
来匹配。anyString()
, anyInt()
等只能匹配非 null
值,查看它们的返回值实际是 "" 和 0 等, 而更为特别的是 any(Foo.class)
看到的是 null
, 仍然不能匹配 null
值。进一步用 Mockito.mockingDetails(mock).printInvocations()
打印出的内容,anyString()
, any(Foo.class)
都会显示为 null
值。
说的有点罗嗦,看下面的例子, 被测试类 UserDao,sql 和 sqlArguments 由各自的 setter 方法来控制,默认它们都为 null
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class UserDao { private final NamedParameterJdbcTemplate jdbcTemplate; private String sql; private MapSqlParameterSource sqlArguments; public void setSql(String sql) { this.sql = sql; } public void setSqlArguments(MapSqlParameterSource sqlArguments) { this.sqlArguments = sqlArguments; } public UserDao(NamedParameterJdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } public List<String> fetchUsers() { return jdbcTemplate.query(sql, sqlArguments, (rs, idx) -> rs.getString("name")); } } |
来它的测试类 UserDaoTest
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
@RunWith(MockitoJUnitRunner.class) public class UserDaoTest { @Mock private NamedParameterJdbcTemplate namedParameterJdbcTemplate; @InjectMocks private UserDao userDao; @Test public void testFetchUsers() { when(namedParameterJdbcTemplate.query(anyString(), any(MapSqlParameterSource.class), ArgumentMatchers.<RowMapper<String>>any())) .thenReturn(Arrays.asList("Hello", "Ketty")); List<String> users = userDao.fetchUsers(); assertThat(users).hasSize(2); } } |
测试不成功,输出信息为
java.lang.AssertionError:
Expected size:<2> but was:<0> in:
<[]>
空为 Mock 对象 namedParameterJdbcTemplate.query(...)
方法返回类型的默认值
原以为 anyString()
和 any(MapSqlParameterSource.class)
可以匹配到实际中的 sql = null
和 sqlArguments = null
值,都说是 any
却不能对应到 null
值。如果在 assertThat(users).hasSize(2)
之前加上
1 |
System.out.println(Mockito.mockingDetails(namedParameterJdbcTemplate).getInvocations()); |
再执行看到的输出为
[namedParameterJdbcTemplate.query(
null,
null,
yanbin.blog.UserDao$$Lambda$1/1192672907@363a52f
);]
mockingDetails 告诉我们前两个参数的实际值为 null, 不能用 anyString()
和 any(MapSqlParameterSource.class)
来匹配.
查看 ArgumentMatchers.anyString() 的值
1 2 3 4 |
public static String anyString() { reportMatcher(new InstanceOf(String.class, "<any string>")); return ""; } |
似乎 anyString()
的值为 "", "" 不能匹配 null
值。断点调试中查看 anyString()
和 any(MapSqlParameterSourcec.class)
的值
注意,查看它们的值时会有异常,即 reportMatcher 报告出来的。
看起来 ArgumentMatchers.any(MapSqlParameterSource.class) 像是个真的 null 值,所以我们下面只把 anyString() 换成 eq(null)
来看下
1 2 3 4 5 6 7 8 9 10 |
@Test public void testFetchUsers() { when(namedParameterJdbcTemplate.query(eq(null), any(MapSqlParameterSource.class), ArgumentMatchers.<RowMapper<String>>any())) .thenReturn(Arrays.asList("Hello", "Ketty")); List<String> users = userDao.fetchUsers(); assertThat(users).hasSize(2); } |
还是不成功,同样的出错信息
java.lang.AssertionError:
Expected size:<2> but was:<0> in:
<[]>
实际运行中的两个 null 都必须用 null
值来匹配
1 2 3 4 5 6 7 8 9 10 |
@Test public void testFetchUsers() { when(namedParameterJdbcTemplate.query(eq(null), eq((MapSqlParameterSource)null), ArgumentMatchers.<RowMapper<String>>any())) .thenReturn(Arrays.asList("Hello", "Ketty")); List<String> users = userDao.fetchUsers(); assertThat(users).hasSize(2); } |
测试通过。eq((MapSqlParameterSource)null) 中的转型是为了避免准确调用重载方法。
最后就是一句话,Mockito 中的 any
不是真正的 any
, any
不代表 null
,有点类似 SQLServer 中的某个字段(c1) 的值为 null
时,where c1 is null
可以查出来,但是用 where c1 in (null)
就查不出来了。