Статьи

Использование EasyMock или Mockito

Я использую EasyMock большую часть времени, но недавно я работал с несколькими людьми, которые были склонны использовать Mockito .

Не намереваясь использовать две фреймворки для одной цели в одном проекте, я принял Mockito .

Итак, последние пару месяцев я использовал Mockito, и вот мой сравнительный анализ этих двух.

Люди, с которыми я работаю, приводят причины читабельности теста для использования Mockitio, но у меня другое мнение по этому поводу. Предположим, у нас есть следующий код, который мы собираемся протестировать:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class MyApp {
    MyService service;
    OtherService otherService;
 
    void operationOne() {
        service.operationOne();
    }
 
    void operationTwo(String args) {
        String operationTwo = otherService.operationTwo(args);
        otherService.operationThree(operationTwo);
    }
 
    void operationThree() {
        service.operationOne();
        otherService.operationThree("success");
    }
}
 
class MyService {
    void operationOne() {}
}
 
class OtherService {
    public String operationTwo(String args) {
        return args;
    }
 
    public void operationThree(String operationTwo) {}
}

Теперь позвольте мне написать простой тестовый пример для этого класса, используя EasyMock и Mockito .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class MyAppEasyMockTest {
    MyApp app;
    MyService service;
    OtherService otherService;
 
    @Before
    public void initialize() {
        service = EasyMock.createMock(MyService.class);
        otherService = EasyMock.createMock(OtherService.class);
        app = new MyApp();
        app.service = service;
        app.otherService = otherService;
    }
 
    @Test
    public void verifySimpleCall() {
        service.operationOne();
        EasyMock.replay(service);
        app.operationOne();
        EasyMock.verify(service);
    }
 }
public class MyAppMockitoTest {
    MyApp app;
    MyService service;
    OtherService otherService;
 
    @Before
    public void initialize() {
        service = Mockito.mock(MyService.class);
        otherService = Mockito.mock(OtherService.class);
        app = new MyApp();
        app.service = service;
        app.otherService = otherService;
    }
 
    @Test
    public void verifySimpleCall() {
        app.operationOne();
        Mockito.verify(service).operationOne();
    }
 }
Это действительно простой тест, и я должен сказать, что тест Mockito более читабелен . Но согласно классической методике тестирования, тест Мокито не завершен. Мы проверили вызов, который ищем, но если завтра я изменю исходный код, добавив еще один вызов в службу, тест не будет нарушен.
1
2
3
4
void operationOne() {
       service.operationOne();
       service.someOtherOp();
 }
Теперь это заставляет меня чувствовать, что тесты недостаточно хороши. Но, к счастью, Mockito предоставляет verifyNoMoreInteractions, которые можно использовать для завершения теста. Теперь позвольте мне написать еще несколько тестов для класса MyApp.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public class MyAppEasyMockTest {
      @Test
    public void verifyMultipleCalls() {
        String args = "one";
        EasyMock.expect(otherService.operationTwo(args)).andReturn(args);
        otherService.operationThree(args);
        EasyMock.replay(otherService);
        app.operationTwo(args);
        EasyMock.verify(otherService);
    }
 
    @Test(expected = RuntimeException.class)
    public void verifyException() {
        service.operationOne();
        EasyMock.expectLastCall().andThrow(new RuntimeException());
        EasyMock.replay(service);
        app.operationOne();
    }
 
    @Test
    public void captureArguments() {
        Capture<String> captured = new Capture<String>();
        service.operationOne();
        otherService.operationThree(EasyMock.capture(captured));
        EasyMock.replay(service, otherService);
        app.operationThree();
        EasyMock.verify(service, otherService);
        assertTrue(captured.getValue().contains("success"));
    }
 
}
 
public class MyAppMockitoTest {
    @Test
    public void verifyMultipleCalls() {
        String args = "one";
        Mockito.when(otherService.operationTwo(args)).thenReturn(args);
        app.operationTwo(args);
        Mockito.verify(otherService).operationTwo(args);
        Mockito.verify(otherService).operationThree(args);
        Mockito.verifyNoMoreInteractions(otherService);
        Mockito.verifyZeroInteractions(service);
    }
 
    @Test(expected = RuntimeException.class)
    public void verifyException() {
        Mockito.doThrow(new RuntimeException()).when(service).operationOne();
        app.operationOne();
    }
 
    @Test
    public void captureArguments() {
        app.operationThree();
        ArgumentCaptor capturedArgs = ArgumentCaptor
                .forClass(String.class);
        Mockito.verify(service).operationOne();
        Mockito.verify(otherService).operationThree(capturedArgs.capture());
        assertTrue(capturedArgs.getValue().contains("success"));
        Mockito.verifyNoMoreInteractions(service, otherService);
    }
}
Это некоторые практические сценарии тестирования, в которых мы хотели бы утверждать аргументы, исключения и т. Д. Если я смотрю и сравниваю те, что написаны с использованием EasyMock, с теми, которые используют Mockito, я склонен чувствовать, что оба теста одинаковы по читаемости, ни один из них не делает лучшая задача.
Большое количество ожидающих и возвратных вызовов в EasyMock делает тесты нечитаемыми, а операторы проверки Mockito часто компрометируют читаемость теста. Согласно документации Mockito, verifyZeroInteractions, verifyNoMoreInteractions не должны использоваться в каждом написанном вами тесте, но если я оставлю их вне своих тестов, то мои тесты будут недостаточно хорошими.
Более того, в тестах все должно быть под контролем разработчика, то есть, как происходит взаимодействие и какие взаимодействия происходят. В EasyMock этот аспект более заметен, так как разработчик должен записать все эти взаимодействия в своем коде, но в Mockito платформа заботится обо всех взаимодействиях, а разработчик просто заинтересован в их проверке (если таковая имеется). Но это может привести к сценариям тестирования, когда разработчик не контролирует все взаимодействия.
У Mockito есть несколько приятных вещей, таких как JunitRunner, которые можно использовать для создания Mocks со всеми необходимыми зависимостями. Это хороший способ удалить часть кода инфраструктуры, и EasyMock также должен иметь такой код.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
@RunWith(MockitoJUnitRunner.class)
public class MyAppMockitoTest {
    MyApp app;
    @Mock
    MyService service;
    @Mock
    OtherService otherService;
 
    @Before
    public void initialize() {
        app = new MyApp();
        app.service = service;
        app.otherService = otherService;
    }
}
Вывод:
Поскольку я использовал обе платформы, я чувствую, что, за исключением простых тестовых случаев, и EasyMock, и Mockito приводят к тестовым примерам, которые равны по читабельности. Но EasyMock лучше для модульного тестирования, так как заставляет разработчика контролировать ситуацию. Mockito из-за своих предположений и соображений скрывает этот контроль под ковром и поэтому не является хорошим выбором. Но Mockito предлагает некоторые вещи, которые весьма полезны (например, junitRunner, цепочка вызовов), и EasyMock должен иметь их в следующем выпуске.

Ссылка: используя EasyMock или Mockito от нашего партнера по JCG Рахула Шарма в блоге The Road пока … блог.