Per l'inizializzazione dei banchi, l'utilizzo del corridore o dello MockitoAnnotations.initMocks
sono soluzioni strettamente equivalenti. Dal javadoc del MockitoJUnitRunner:
JUnit 4.5 runner initializes mocks annotated with Mock, so that explicit usage of MockitoAnnotations.initMocks(Object) is not necessary. Mocks are initialized before each test method.
La prima soluzione (con la MockitoAnnotations.initMocks
) potrebbe essere utilizzato quando si è già configurato un corridore specifico (SpringJUnit4ClassRunner
per esempio) sul banco di prova.
La seconda soluzione (con il MockitoJUnitRunner
) è la più classica e la mia preferita. Il codice è più semplice. L'utilizzo di un corridore offre il grande vantaggio di automatic validation of framework usage (descritto da @David Wallace in this answer).
Entrambe le soluzioni consentono di condividere i mock (e spie) tra i metodi di test. Abbinati allo @InjectMocks
, consentono di scrivere test di unità molto rapidamente. Il codice mocking del boilerplate è ridotto, i test sono più facili da leggere. Per esempio:
@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));
}
}
Pro: Il codice è minima
Contro: magia nera. IMO è dovuto principalmente all'annotazione @InjectMocks. Con questa annotazione "si perde il dolore di codice" (vedere le grandi commenti di @Brice)
La terza soluzione è quella di creare la vostra finta su ogni metodo di prova. Permette come spiegato da @mlk nella sua risposta di avere "test autonomo".
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));
}
}
Pro: È dimostrano chiaramente come funziona il vostro API (BDD) ...
Contro: non v'è più codice boilerplate. (La creazione prende in giro)
mio sicuramente al livello è un compromesso.Utilizzare il @Mock
annotazioni con il @RunWith(MockitoJUnitRunner.class)
, ma non utilizzare i @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));
}
}
Pro: È dimostrano chiaramente come funziona il vostro API (come il mio ArticleManager
viene istanziato). Nessun codice standard.
Contro: Il test non è autonomo, meno dolore di codice
Attenzione però, le annotazioni sono utili, ma non proteggono contro le imbarcazioni da cattiva progettazione OO (o degradarlo). Personalmente, mentre sono felice di ridurre il codice boilerplate, perdo il dolore del codice (o PITA) che è il grilletto per cambiare il design in uno migliore, così io e il team prestiamo attenzione al design OO. Sento che seguire il design OO con principi come il design SOLID o le idee GOOS è molto più importante che scegliere come istanziare i mock. – Brice
@Brice perché? se la classe sotto test ('ArticleManager') ha troppe dipendenze, la vedrò chiaramente. E questo è fuori tema, la domanda è solo "come istanziare i mock?" – gontard
Avrei dovuto dire "meno ovvio" :) Quindi in realtà @injectMocks proverà il meglio per iniettare i mock automaticamente senza che l'utente debba collegare le cose (come siamo abituati a fare in primavera o in guisa), quindi l'istanza dell'oggetto è nascosta, non si sa se è l'iniezione del costruttore o l'iniezione setter, che potrebbe essere fastidioso per l'uso futuro di questo oggetto (la riusabilità è uno dei principali vantaggi del buon design OO). – Brice