Para la inicialización de los simulacros , usar el corredor o MockitoAnnotations.initMocks
son soluciones estrictamente equivalentes. Desde el javadoc de 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 primera solución (con el MockitoAnnotations.initMocks
) podría usarse cuando ya haya configurado un corredor específico ( SpringJUnit4ClassRunner
por ejemplo) en su caso de prueba.
La segunda solución (con la MockitoJUnitRunner
) es la más clásica y mi favorita. El código es más sencillo. El uso de un corredor proporciona la gran ventaja de la validación automática del uso del marco (descrito por @David Wallace en esta respuesta ).
Ambas soluciones permiten compartir los simulacros (y espías) entre los métodos de prueba. Junto con @InjectMocks
, permiten escribir pruebas unitarias muy rápidamente. El código burlón repetitivo se reduce, las pruebas son más fáciles de leer. Por ejemplo:
@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));
}
}
Ventajas: el código es mínimo
Contras: magia negra. En mi opinión, se debe principalmente a la anotación @InjectMocks. Con esta anotación "pierdes el dolor del código" (mira los grandes comentarios de @Brice )
La tercera solución es crear su simulacro de cada método de prueba. Permite, como explica @mlk en su respuesta, tener una " prueba autónoma ".
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));
}
}
Ventajas: demuestra claramente cómo funciona su api (BDD ...)
Contras: hay más código repetitivo. (La creación de las burlas)
Mi recomendación es un compromiso. Utilice la @Mock
anotación con @RunWith(MockitoJUnitRunner.class)
, pero no utilice @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));
}
}
Ventajas: demuestra claramente cómo funciona su api (cómo ArticleManager
se crea una instancia de my ). Sin código repetitivo.
Contras: la prueba no es autónoma, menos dolor de código