Я использую 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 пока … блог.