«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() для сопоставления с новым добавленным пользователем:

@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() сравнивает аргументы, используя «==» , что означает, что в нашем случае он сравнивает экземпляры пользователя.

Если мы не используем сопоставления, аргументы по умолчанию сравниваются с помощью equals().

Для массивов у нас также есть сопоставитель aryEq(), основанный на методе Arrays.equals().

4. Any Matchers

Существует несколько any matchers, таких как anyInt(), anyBoolean(), anyDouble(),… и т. д. Они указывают, что аргумент должен иметь заданный тип.

Давайте рассмотрим пример использования anyString() для сопоставления ожидаемого электронного письма с любым строковым значением:

@Test
public void givenUserService_whenSearchForUserByEmail_thenFound() {
    expect(userService.findByEmail(anyString()))
      .andReturn(Collections.emptyList());
    replay(userService);

    List<User> result = userService.findByEmail("[email protected]");
    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. Сопоставители Null

Далее мы можем использовать сопоставители isNull() и notNull() для сопоставления нулевых значений.

В следующем примере мы будем использовать сопоставитель 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. Сопоставители строк

Есть несколько полезных сопоставлений, которые мы можем использовать со строковыми аргументами.

Во-первых, мы будем использовать сопоставитель startWith() для сопоставления с префиксом электронной почты пользователя:

@Test
public void whenSearchForUserByEmailStartsWith_thenFound() {        
    expect(userService.findByEmail(startsWith("test")))
      .andReturn(Collections.emptyList());
    replay(userService);

    List<User> result = userService.findByEmail("[email protected]");
    verify(userService);
    assertEquals(0,result.size());
}

Точно так же мы будем использовать сопоставитель endWith() для суффикса электронной почты:


@Test
public void givenUserService_whenSearchForUserByEmailEndsWith_thenFound() {        
    expect(userService.findByEmail(endsWith(".com")))
      .andReturn(Collections.emptyList());
    replay(userService);

    List<User> result = userService.findByEmail("[email protected]");
    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("[email protected]");
    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("[email protected]");
    verify(userService);
    assertEquals(0,result.size());
}

7. Сопоставление чисел

У нас также есть несколько сопоставителей для числовых значений, которые мы можем использовать.

Давайте посмотрим на пример использования сопоставителя lt() для сопоставления аргумента age с значением меньше 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() для сопоставления аргумента age с значением больше или равно 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: электронные письма, которые не заканчиваются на «.com»:


@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());
}

9. Custom Matcher

@Test
public void givenUserService_whenSearchForUserByEmailNotEndsWith_thenFound() {
    expect(userService.findByEmail(not(endsWith(".com"))))
      .andReturn(Collections.emptyList());
    replay(userService);

    List<User> result = userService.findByEmail("[email protected]");
    verify(userService);
    assertEquals(0,result.size());
}

Наконец, мы обсудим, как создать собственный сопоставление EasyMock.

Цель состоит в том, чтобы создать простой сопоставитель minCharCount() для сопоставления строк, длина которых больше или равна заданному значению:

Чтобы создать собственный сопоставитель аргументов, нам необходимо:

@Test
public void givenUserService_whenSearchForUserByEmailCharCount_thenFound() {        
    expect(userService.findByEmail(minCharCount(5)))
      .andReturn(Collections.emptyList());
    replay(userService);

    List<User> result = userService.findByEmail("[email protected]");
    verify(userService);
    assertEquals(0,result.size());
}

создать новый класс, реализующий интерфейс IArgumentMatcher, создайте статический метод с новым именем сопоставления и зарегистрируйте экземпляр указанного выше класса, используя reportMatcher()

    Давайте посмотрим на оба шага в нашем методе minCharCount(), который объявляет в нем анонимный класс:

Также обратите внимание, что интерфейс IArgumentMatcher имеет два метода:match() и appendTo().

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;
}

«Первый метод содержит проверку аргументов и логику для нашего сопоставителя, а второй используется для добавления строкового представления сопоставителя, которое будет напечатано в случае сбоя.

10. Заключение

Мы рассмотрели предопределенные сопоставители аргументов EasyMock для различных типов данных и то, как создать собственный сопоставитель.

Полный исходный код примеров доступен на GitHub.


«