Pour l'initialisation des mocks en utilisant le coureur ou le MockitoAnnotations.initMocks
sont des solutions strictement équivalentes. D'après la javadoc du MockitoJUnitRunner :
Le runner de JUnit 4.5 initialise les mocks annotés avec Mock, de sorte que l'utilisation explicite de MockitoAnnotations.initMocks(Object) n'est pas nécessaire. Les mocks sont initialisés avant chaque méthode de test.
La première solution (avec le MockitoAnnotations.initMocks
) pourrait être utilisé lorsque vous avez déjà configuré un coureur spécifique ( SpringJUnit4ClassRunner
par exemple) sur votre scénario de test.
La deuxième solution (avec le MockitoJUnitRunner
) est le plus classique et mon préféré. Le code est plus simple. L'utilisation d'un runner offre le grand avantage de [validation automatique de l'utilisation du cadre](https://www.javadoc.io/doc/org.mockito/mockito-core/2.2.28/org/mockito/Mockito.html#validateMockitoUsage()) (décrit par David Wallace en cette réponse ).
Les deux solutions permettent de partager les mocks (et les espions) entre les méthodes de test. Couplé avec le @InjectMocks
ils permettent d'écrire des tests unitaires très rapidement. Le code de simulation passe-partout est réduit, les tests sont plus faciles à lire. Par exemple :
@RunWith(MockitoJUnitRunner.class)
public class ArticleManagerTest {
@Mock private ArticleCalculator calculator;
@Mock(name = "database") private ArticleDatabase dbMock;
@Spy private UserProvider userProvider = new ConsumerUserProvider();
@InjectMocks private ArticleManager manager;
@Test public void shouldDoSomething() {
manager.initiateArticle();
verify(database).addListener(any(ArticleListener.class));
}
@Test public void shouldDoSomethingElse() {
manager.finishArticle();
verify(database).removeListener(any(ArticleListener.class));
}
}
Pros : Le code est minimal
Inconvénients : magie noire. IMO, c'est principalement dû à l'annotation @InjectMocks. Avec cette annotation "vous perdez la douleur du code" (voir les excellents commentaires de @Brice )
La troisième solution consiste à créer votre objet fantaisie sur chaque méthode de test. Il permet comme expliqué par @mlk dans sa réponse pour avoir " essai autonome ".
public class ArticleManagerTest {
@Test public void shouldDoSomething() {
// given
ArticleCalculator calculator = mock(ArticleCalculator.class);
ArticleDatabase database = mock(ArticleDatabase.class);
UserProvider userProvider = spy(new ConsumerUserProvider());
ArticleManager manager = new ArticleManager(calculator,
userProvider,
database);
// when
manager.initiateArticle();
// then
verify(database).addListener(any(ArticleListener.class));
}
@Test public void shouldDoSomethingElse() {
// given
ArticleCalculator calculator = mock(ArticleCalculator.class);
ArticleDatabase database = mock(ArticleDatabase.class);
UserProvider userProvider = spy(new ConsumerUserProvider());
ArticleManager manager = new ArticleManager(calculator,
userProvider,
database);
// when
manager.finishArticle();
// then
verify(database).removeListener(any(ArticleListener.class));
}
}
Pros : Vous démontrez clairement le fonctionnement de votre API (BDD...).
Contre : il y a plus de code passe-partout. (La création de mocks)
Mon La recommandation est un compromis. Utilisez le @Mock
avec l'annotation @RunWith(MockitoJUnitRunner.class)
mais n'utilisez pas l'option @InjectMocks
:
@RunWith(MockitoJUnitRunner.class)
public class ArticleManagerTest {
@Mock private ArticleCalculator calculator;
@Mock private ArticleDatabase database;
@Spy private UserProvider userProvider = new ConsumerUserProvider();
@Test public void shouldDoSomething() {
// given
ArticleManager manager = new ArticleManager(calculator,
userProvider,
database);
// when
manager.initiateArticle();
// then
verify(database).addListener(any(ArticleListener.class));
}
@Test public void shouldDoSomethingElse() {
// given
ArticleManager manager = new ArticleManager(calculator,
userProvider,
database);
// when
manager.finishArticle();
// then
verify(database).removeListener(any(ArticleListener.class));
}
}
Pros : Vous démontrez clairement le fonctionnement de votre api (Comment mon ArticleManager
est instancié). Pas de code passe-partout.
Inconvénients : Le test n'est pas autonome, ce qui réduit la douleur du code.