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
MockitoJUnitRunner
. Para obtener más información sobre las diferencias, consulte la pregunta en stackoverflow.com/questions/10806345/… y mi respuesta.Collaborator collab = mock(Collaborator.class)
, en mi opinión, esta forma es sin duda un enfoque válido. Si bien esto puede tender a ser detallado, puede ganar en comprensibilidad y refactorabilidad de las pruebas. Ambas formas tienen sus pros y sus contras, todavía no he decidido cuál es mejor. Amyway siempre es posible escribir basura, y probablemente depende del contexto y del codificador.Ahora hay (a partir de v1.10.7) una cuarta forma de instanciar simulacros, que está utilizando una regla JUnit4 llamada MockitoRule .
JUnit busca subclases de TestRule anotadas con @Rule y las usa para envolver las declaraciones de prueba que proporciona el Runner . El resultado de esto es que puede extraer métodos @Before, métodos @After e incluso intentar ... atrapar envoltorios en reglas. Incluso puede interactuar con estos desde dentro de su prueba, de la forma en que lo hace ExpectedException .
MockitoRule se comporta casi exactamente como MockitoJUnitRunner , excepto que puede usar cualquier otro corredor, como Parameterized (que permite que sus constructores de prueba tomen argumentos para que sus pruebas se puedan ejecutar varias veces), o el corredor de pruebas de Robolectric (para que su cargador de clases pueda proporcionar reemplazos de Java para clases nativas de Android). Esto lo hace estrictamente más flexible de usar en versiones recientes de JUnit y Mockito.
En resumen:
Mockito.mock()
: Invocación directa sin soporte de anotaciones ni validación de uso.MockitoAnnotations.initMocks(this)
: Soporte de anotaciones, sin validación de uso.MockitoJUnitRunner
: Soporte de anotaciones y validación de uso, pero debe usar ese corredor.MockitoRule
: Soporte de anotaciones y validación de uso con cualquier corredor JUnit.Vea también: ¿Cómo funciona JUnit @Rule?
fuente
@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
Hay una forma sencilla de hacer esto.
Si es una prueba unitaria, puede hacer esto:
EDITAR: Si se trata de una prueba de integración, puede hacer esto (no está destinado a usarse de esa manera con Spring. Solo muestre que puede inicializar simulacros con diferentes Runners):
fuente
MockitoAnnotations y el corredor se han discutido bien anteriormente, así que voy a arrojar mi tuppence por los no amados:
Utilizo esto porque lo encuentro un poco más descriptivo y prefiero (no la prohibición correcta) que las pruebas unitarias no utilicen variables miembro, ya que me gusta que mis pruebas sean (en la medida de lo posible) autónomas.
fuente
Un pequeño ejemplo para JUnit 5 Jupiter, se eliminó "RunWith", ahora necesita usar las extensiones usando la anotación "@ExtendWith".
fuente
Las otras respuestas son excelentes y contienen más detalles si las desea o las necesita.
Además de esos, me gustaría agregar un TL; DR:
@RunWith(MockitoJUnitRunner.class)
@Rule public MockitoRule rule = MockitoJUnit.rule();
@Before public void initMocks() { MockitoAnnotations.initMocks(this); }
X x = mock(X.class)
(1) y (2) y (3) son mutuamente excluyentes.
(4) se puede utilizar en combinación con los demás.
fuente