1. 概述
本文将深入探讨 EasyMock 参数匹配器的使用技巧。我们将系统介绍各类预定义匹配器,并演示如何创建自定义匹配器。
如果你对 EasyMock 基础还不熟悉,建议先阅读我们的 EasyMock 入门指南。本文假设你已经掌握了 EasyMock 的基本用法。
2. 基础 Mock 示例
在探索各种匹配器之前,先明确我们的测试场景。本文所有示例都基于一个简单的用户服务。
这是我们的核心接口 IUserService
:
public interface IUserService {
public boolean addUser(User user);
public List<User> findByEmail(String email);
public List<User> findByAge(double age);
}
对应的 User
实体类:
public class User {
private long id;
private String firstName;
private String lastName;
private double age;
private String email;
// 标准构造器、getter/setter 方法
}
首先创建基础 mock 对象供后续使用:
private IUserService userService = mock(IUserService.class);
现在开始探索 EasyMock 的参数匹配器世界。
3. 等值匹配器
使用 eq()
匹配器验证新增用户:
@Test
public void givenUserService_whenAddNewUser_thenOK() {
expect(userService.addUser(eq(new User()))).andReturn(true);
replay(userService);
boolean result = userService.addUser(new User());
verify(userService);
assertTrue(result);
}
✅ 该匹配器支持基本类型和对象,对象比较使用 equals()
方法。
使用 same()
匹配器验证特定用户实例:
@Test
public void givenUserService_whenAddSpecificUser_thenOK() {
User user = new User();
expect(userService.addUser(same(user))).andReturn(true);
replay(userService);
boolean result = userService.addUser(user);
verify(userService);
assertTrue(result);
}
⚠️ same()
使用 ==
比较对象引用,而非值比较。
默认情况下(不使用匹配器时),EasyMock 使用 equals()
进行参数比较。对于数组,可使用基于 Arrays.equals()
的 aryEq()
匹配器。
4. 任意类型匹配器
提供多种 any
系列匹配器:anyInt()
、anyBoolean()
、anyDouble()
等。这些匹配器要求参数为指定类型。
示例:使用 anyString()
匹配任意邮箱字符串:
@Test
public void givenUserService_whenSearchForUserByEmail_thenFound() {
expect(userService.findByEmail(anyString()))
.andReturn(Collections.emptyList());
replay(userService);
List<User> result = userService.findByEmail("test@example.com");
verify(userService);
assertEquals(0,result.size());
}
使用 isA()
验证参数类型:
@Test
public void givenUserService_whenAddUser_thenOK() {
expect(userService.addUser(isA(User.class))).andReturn(true);
replay(userService);
boolean result = userService.addUser(new User());
verify(userService);
assertTrue(result);
}
这里我们断言 addUser()
方法的参数必须是 User
类型。
5. 空值匹配器
使用 isNull()
和 notNull()
匹配 null
值:
@Test
public void givenUserService_whenAddNull_thenFail() {
expect(userService.addUser(isNull())).andReturn(false);
replay(userService);
boolean result = userService.addUser(null);
verify(userService);
assertFalse(result);
}
类似地,使用 notNull()
验证非空值:
@Test
public void givenUserService_whenAddNotNull_thenOK() {
expect(userService.addUser(notNull())).andReturn(true);
replay(userService);
boolean result = userService.addUser(new User());
verify(userService);
assertTrue(result);
}
6. 字符串匹配器
提供多个专用字符串匹配器:
使用 startsWith()
匹配邮箱前缀:
@Test
public void whenSearchForUserByEmailStartsWith_thenFound() {
expect(userService.findByEmail(startsWith("test")))
.andReturn(Collections.emptyList());
replay(userService);
List<User> result = userService.findByEmail("test@example.com");
verify(userService);
assertEquals(0,result.size());
}
使用 endsWith()
匹配邮箱后缀:
@Test
public void givenUserService_whenSearchForUserByEmailEndsWith_thenFound() {
expect(userService.findByEmail(endsWith(".com")))
.andReturn(Collections.emptyList());
replay(userService);
List<User> result = userService.findByEmail("test@example.com");
verify(userService);
assertEquals(0,result.size());
}
使用 contains()
匹配包含子串的邮箱:
@Test
public void givenUserService_whenSearchForUserByEmailContains_thenFound() {
expect(userService.findByEmail(contains("@")))
.andReturn(Collections.emptyList());
replay(userService);
List<User> result = userService.findByEmail("test@example.com");
verify(userService);
assertEquals(0,result.size());
}
使用 matches()
进行正则匹配:
@Test
public void givenUserService_whenSearchForUserByEmailMatches_thenFound() {
expect(userService.findByEmail(matches(".+\\@.+\\..+")))
.andReturn(Collections.emptyList());
replay(userService);
List<User> result = userService.findByEmail("test@example.com");
verify(userService);
assertEquals(0,result.size());
}
7. 数值匹配器
提供多个数值专用匹配器:
使用 lt()
匹配小于 100 的年龄:
@Test
public void givenUserService_whenSearchForUserByAgeLessThan_thenFound() {
expect(userService.findByAge(lt(100.0)))
.andReturn(Collections.emptyList());
replay(userService);
List<User> result = userService.findByAge(20);
verify(userService);
assertEquals(0,result.size());
}
使用 geq()
匹配大于等于 10 的年龄:
@Test
public void givenUserService_whenSearchForUserByAgeGreaterThan_thenFound() {
expect(userService.findByAge(geq(10.0)))
.andReturn(Collections.emptyList());
replay(userService);
List<User> result = userService.findByAge(20);
verify(userService);
assertEquals(0,result.size());
}
可用数值匹配器清单:
lt()
– 小于指定值leq()
– 小于等于gt()
– 大于geq()
– 大于等于
8. 组合匹配器
使用 and()
、or()
和 not()
组合多个匹配器:
组合 gt()
和 lt()
验证年龄范围:
@Test
public void givenUserService_whenSearchForUserByAgeRange_thenFound() {
expect(userService.findByAge(and(gt(10.0),lt(100.0))))
.andReturn(Collections.emptyList());
replay(userService);
List<User> result = userService.findByAge(20);
verify(userService);
assertEquals(0,result.size());
}
组合 not()
和 endsWith()
排除 .com
后缀邮箱:
@Test
public void givenUserService_whenSearchForUserByEmailNotEndsWith_thenFound() {
expect(userService.findByEmail(not(endsWith(".com"))))
.andReturn(Collections.emptyList());
replay(userService);
List<User> result = userService.findByEmail("test@example.org");
verify(userService);
assertEquals(0,result.size());
}
9. 自定义匹配器
最后演示如何创建自定义匹配器。目标实现 minCharCount()
匹配器,验证字符串长度:
@Test
public void givenUserService_whenSearchForUserByEmailCharCount_thenFound() {
expect(userService.findByEmail(minCharCount(5)))
.andReturn(Collections.emptyList());
replay(userService);
List<User> result = userService.findByEmail("test@example.com");
verify(userService);
assertEquals(0,result.size());
}
创建自定义匹配器需要两个步骤:
- 实现
IArgumentMatcher
接口 - 创建静态方法并通过
reportMatcher()
注册匹配器实例
实现代码如下(使用匿名内部类):
public static String minCharCount(int value){
EasyMock.reportMatcher(new IArgumentMatcher() {
@Override
public boolean matches(Object argument) {
return argument instanceof String
&& ((String) argument).length() >= value;
}
@Override
public void appendTo(StringBuffer buffer) {
buffer.append("charCount(\"" + value + "\")");
}
});
return null;
}
❌ 注意:IArgumentMatcher
接口包含两个方法:
matches()
:包含参数验证逻辑appendTo()
:定义匹配器在错误信息中的字符串表示
10. 总结
我们系统学习了 EasyMock 的预定义参数匹配器,并掌握了创建自定义匹配器的方法。合理使用匹配器能让单元测试更简洁、更健壮。
完整示例代码请查看 GitHub 项目。