Je ne discuterai pas de la lisibilité, de la taille ou de la technique de test de ces cadres, je pense qu'ils sont égaux. Mais, je vais vous montrer quelques faits qui sont distingués sur l'exemple :
Donné : Nous avons une classe suivante qui est responsable de stocker quelque chose quelque part :
public class Service {
public static final String PATH = "path";
public static final String NAME = "name";
public static final String CONTENT = "content";
private FileDao dao;
public void doSomething() {
dao.store(PATH, NAME, IOUtils.toInputStream(CONTENT));
}
public void setDao(FileDao dao) {
this.dao = dao;
}
}
Et nous voulons le tester :
Mockito :
public class ServiceMockitoTest {
private Service service;
@Mock
private FileDao dao;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
service = new Service();
service.setDao(dao);
}
@Test
public void testDoSomething() throws Exception {
// given
// when
service.doSomething();
// then
ArgumentCaptor<InputStream> captor = ArgumentCaptor.forClass(InputStream.class);
Mockito.verify(dao, times(1)).store(eq(Service.PATH), eq(Service.NAME), captor.capture());
assertThat(Service.CONTENT, is(IOUtils.toString(captor.getValue())));
}
}
EasyMock :
public class ServiceEasyMockTest {
private Service service;
private FileDao dao;
@Before
public void setUp() {
dao = EasyMock.createNiceMock(FileDao.class);
service = new Service();
service.setDao(dao);
}
@Test
public void testDoSomething() throws Exception {
// given
Capture<InputStream> captured = new Capture<InputStream>();
dao.store(eq(Service.PATH), eq(Service.NAME), capture(captured));
replay(dao);
// when
service.doSomething();
// then
assertThat(Service.CONTENT, is(IOUtils.toString(captured.getValue())));
verify(dao);
}
}
Comme vous pouvez le constater, les tests sont relativement identiques et les deux sont réussis. Imaginons que quelqu'un d'autre ait changé l'implémentation du service et essaie d'exécuter les tests.
La nouvelle mise en œuvre ressemble :
dao.store(PATH + separator, NAME, IOUtils.toInputStream(CONTENT));
a été ajouté à la fin du chemin
Quel sera le résultat des tests maintenant ? Les deux échouent, mais avec des messages d'erreur différents :
EasyMock :
java.lang.AssertionError: Nothing captured yet
at org.easymock.Capture.getValue(Capture.java:78)
at ServiceEasyMockTest.testDoSomething(ServiceEasyMockTest.java:36)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
Mockito :
Argument(s) are different! Wanted:
dao.store(
"path",
"name",
<Capturing argument>
);
-> at ServiceMockitoTest.testDoSomething(ServiceMockitoTest.java:34)
Actual invocation has different arguments:
dao.store(
"path\",
"name",
java.io.ByteArrayInputStream@1c99159
);
-> at Service.doSomething(Service.java:13)
<Click to see difference>
Que s'est-il passé dans le test EasyMock, pourquoi le résultat n'a-t-il pas été capturé ? La méthode store n'a pas été exécutée, mais attendez une minute, elle l'a été, pourquoi EasyMock nous ment-il ?
C'est parce qu'EasyMock mélange deux responsabilités dans une seule ligne - stubbing et vérification. C'est pourquoi, lorsque quelque chose ne va pas, il est difficile de comprendre quelle partie a causé l'échec.
Bien sûr, vous pouvez me dire - il suffit de changer le test et de déplacer la vérification avant l'assertion. Wow, vous êtes sérieux, les développeurs doivent garder à l'esprit un ordre magique imposé par le framework mocking ?
Au fait, ça ne servira à rien :
java.lang.AssertionError:
Expectation failure on verify:
store("path", "name", capture(Nothing captured yet)): expected: 1, actual: 0
at org.easymock.internal.MocksControl.verify(MocksControl.java:111)
at org.easymock.classextension.EasyMock.verify(EasyMock.java:211)
Pourtant, il me semble que la méthode n'a pas été exécutée, alors qu'elle l'a été, mais avec d'autres paramètres.
Pourquoi Mockito est meilleur, parce que ce framework ne mélange pas deux responsabilités en un seul endroit et quand vos tests échoueront, vous comprendrez facilement pourquoi.