Contents

EASYMOCK参数匹配器

1. 概述

在本教程中,我们将探索 EasyMock 参数匹配器。我们将讨论不同类型的预定义匹配器以及如何创建自定义匹配器

我们已经在 介绍 EasyMock 的文章中介绍了 EasyMock 的基础知识,因此您可能需要先阅读它以熟悉 EasyMock。

2. 简单的模拟示例

在我们开始探索不同的匹配器之前,让我们看一下我们的上下文。在本教程中,我们将在示例中使用非常基本的用户服务。

这是我们简单的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;
    // standard constructor, getters, setters
}

因此,我们将首先模拟我们的IUserService以在我们的示例中使用它:

private IUserService userService = mock(IUserService.class);

现在,让我们探索 EasyMock 参数匹配器。

3. 等于匹配器

首先,我们将使用 *eq()*匹配器来匹配新添加的 User

@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()*匹配器来匹配特定的 User

@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()*匹配器使用“ ==” 比较参数,这意味着它在我们的例子中比较User*实例。

如果我们不使用任何匹配器,则默认使用equals() 比较参数。

对于数组,我们还有基于*Arrays.equals()方法的aryEq()*匹配器。

4. 任何匹配器

有多个 any 匹配器,例如anyInt()anyBoolean()、*anyDouble()*等。它们指定参数应该具有给定的类型。

让我们看一个使用 anyString()将预期Email匹配 为任何string值的示例:

@Test
public void givenUserService_whenSearchForUserByEmail_thenFound() {
    expect(userService.findByEmail(anyString()))
      .andReturn(Collections.emptyList());
    replay(userService);
    List<User> result = userService.findByEmail("blogdemo@itcodingman.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值。

在以下示例 中,如果添加的User值为 null ,我们将使用*isNull()*匹配器进行匹配:

@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. 字符串匹配器

我们可以将多个有用的匹配器与string参数一起使用。

首先,我们将使用 *startsWith()*匹配器来匹配用户的电子邮件前缀:

@Test
public void whenSearchForUserByEmailStartsWith_thenFound() {        
    expect(userService.findByEmail(startsWith("test")))
      .andReturn(Collections.emptyList());
    replay(userService);
    List<User> result = userService.findByEmail("blogdemo@itcodingman.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("blogdemo@itcodingman.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("blogdemo@itcodingman.com");
    verify(userService);
    assertEquals(0,result.size());
}

甚至使用 *match()*将我们的电子邮件与特定的正则表达式匹配:

@Test
public void givenUserService_whenSearchForUserByEmailMatches_thenFound() {        
    expect(userService.findByEmail(matches(".+\\@.+\\..+")))
      .andReturn(Collections.emptyList());
    replay(userService);
    List<User> result = userService.findByEmail("blogdemo@itcodingman.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()匹配器组合多个匹配器。

让我们看看如何结合两个匹配器来验证年龄值既大于 10 又小于 100:

@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("blogdemo@itcodingman.com");
    verify(userService);
    assertEquals(0,result.size());
}

9. 自定义匹配器

最后,我们将讨论如何创建自定义 EasyMock 匹配器。

目标是创建一个简单的*minCharCount()*匹配器来匹配长度大于或等于给定值的字符串:

@Test
public void givenUserService_whenSearchForUserByEmailCharCount_thenFound() {        
    expect(userService.findByEmail(minCharCount(5)))
      .andReturn(Collections.emptyList());
    replay(userService);
    List<User> result = userService.findByEmail("blogdemo@itcodingman.com");
    verify(userService);
    assertEquals(0,result.size());
}

要创建自定义参数匹配器,我们需要:

  • 创建一个实现IArgumentMatcher接口的新类
  • 使用新的匹配器名称创建一个静态方法并使用reportMatcher() 注册上述类的实例

让我们看看 *minCharCount()*方法中的两个步骤,它在其中声明了一个匿名类:

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()

第一个方法包含我们的匹配器的参数验证和逻辑,而第二个用于附加匹配器字符串表示以在失败的情况下打印。