Varias declaraciones RunWith en jUnit

113

Escribo una prueba de unidad y quiero usar JUnitParamsRunnery MockitoJUnitRunnerpara una clase de prueba.

Desafortunadamente, lo siguiente no funciona:

@RunWith(MockitoJUnitRunner.class)
@RunWith(JUnitParamsRunner.class)
public class DatabaseModelTest {
  // some tests
}

¿Hay alguna forma de usar Mockito y JUnitParams en una clase de prueba?

Hans-Helge
fuente
2
También hay un buen ejemplo aquí: blog.project13.pl/index.php/coding/1077/…
falsarella

Respuestas:

110

No puede hacer esto porque, de acuerdo con las especificaciones, no puede poner la misma anotación dos veces en el mismo elemento anotado.

¿Entonces, cuál es la solución? La solución es poner solo uno @RunWith()con un corredor sin el que no pueda permanecer y reemplazar el otro con otra cosa. En su caso, supongo que eliminará MockitoJUnitRunnery hará programáticamente lo que hace.

De hecho, lo único que hace es ejecutar:

MockitoAnnotations.initMocks(test);

al comienzo del caso de prueba. Entonces, la solución más simple es poner este código en el setUp()método:

@Before
public void setUp() {
    MockitoAnnotations.initMocks(this);
}

No estoy seguro, pero probablemente debería evitar llamadas múltiples de este método usando flag:

private boolean mockInitialized = false;
@Before
public void setUp() {
    if (!mockInitialized) {
        MockitoAnnotations.initMocks(this);
        mockInitialized = true;  
    }
}

Sin embargo, se puede implementar una solución reutilizable mejor con las reglas de JUnt.

public class MockitoRule extends TestWatcher {
    private boolean mockInitialized = false;

    @Override
    protected void starting(Description d) {
        if (!mockInitialized) {
            MockitoAnnotations.initMocks(this);
            mockInitialized = true;  
        }
    }
}

Ahora solo agregue la siguiente línea a su clase de prueba:

@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();

y puede ejecutar este caso de prueba con cualquier corredor que desee.

AlexR
fuente
12
El cheque mockInitializedes incorrecto. Quieres tener una nueva burla para cada prueba.
BetaRide
1
@BetaRide, depende de tus necesidades. A veces desea inicializar simulacro cada vez, a veces no.
AlexR
Si desea configurar una vez por archivo de clase, puede usar BeforeClass en lugar de Before, que se invocará una vez y solo una vez por archivo de prueba.
InfernalRapture
56

A partir de JUnit 4.7 y Mockito 1.10.17, esta funcionalidad está incorporada; hay una org.mockito.junit.MockitoRuleclase. Simplemente puede importarlo y agregar la línea

@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();

a tu clase de prueba.

Erica Kane
fuente
2
Para versiones anteriores de Mockito (hasta la 1.10.5 al parecer), debe usar:@Rule public MockitoJUnitRule mockito = new MockitoJUnitRule(this);
Cliff Sun
MockitoAnnotations.initMocks(this)es muy lento para crear simulaciones. La forma más eficiente es usar @Runwith (MockitoJunitRunner.class)
ant2009
16

Esta solución funciona para todos los corredores posibles, no solo para este ejemplo de mockito. Por ejemplo; para Spring, simplemente cambie las clases de corredor y agregue las anotaciones necesarias.

@RunWith(JUnitParamsRunner.class)
public class DatabaseModelTest {

    @Test
    public void subRunner() throws Exception {
        JUnitCore.runClasses(TestMockitoJUnitRunner.class);
    }

    @RunWith(MockitoJUnitRunner.class)
    public static class TestMockitoJUnitRunner {
    }
}

DatabaseModelTestserá ejecutado por JUnit. TestMockitoJUnitRunnerdepende de él (por lógica) y se ejecutará dentro del main en un @Testmétodo, durante la llamada JUnitCore.runClasses(TestMockitoJUnitRunner.class). Este método garantiza que el corredor principal se inicie correctamente antes static class TestMockitoJUnitRunnerde que se ejecute el sub corredor, implementando de manera efectiva múltiples @RunWithanotaciones anidadas con clases de prueba dependientes.

También en https://bekce.github.io/junit-multiple-runwith-dependent-tests

bekce
fuente
3
Al llamar JUnitCore.runClasses()sin inspeccionar el resultado, corre el riesgo de enmascarar los errores de la prueba interna. assert(JUnitCore.runClasses(TestMockitoJUnitRunner.class).wasSuccessful());al menos le informará el error
Robotnik
2

En mi caso, estaba tratando de simular algún método en spring bean y

MockitoAnnotations.initMocks(test);

no funciona. En su lugar, debe definir ese bean para que se construya utilizando un método simulado dentro de su archivo xml como sigue.

...
<bean id="classWantedToBeMocked" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.fullpath.ClassWantedToBeMocked" />
</bean>
...

y agregue ese bean con autowired dentro de su clase de prueba como sigue.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="file:springconfig.xml")
public class TestClass {
    ...
    @Autowired
    private ClassWantedToBeMocked classWantedToBeMocked;
    ...
    when(classWantedToBeMocked.methodWantedToBeMocked()).thenReturn(...);
    ...
}
Heungwoo
fuente
0

Echa un vistazo a este enlace https://bekce.github.io/junit-multiple-runwith-dependent-tests/ usando este enfoque combiné un @RunWith (Parameterized.class) - corredor externo - con @RunWith (MockitoJUnitRunner.class) - corredor interior. El único ajuste que tuve que agregar fue hacer que mis variables miembro en la clase externa / corredor estén estáticas para que sean accesibles para el corredor / clase interno / anidado. buena suerte y disfruta.

Legna
fuente
0

Quería ejecutar SWTBotJunit4ClassRunner y org.junit.runners.Parameterized al mismo tiempo, tengo pruebas paramétricas y quiero capturas de pantalla cuando falla la prueba SWT (la función de captura de pantalla la proporciona SWTBotJunit4ClassRunner ). La respuesta de @bekce es excelente y primero quería ir por ese camino, pero fue peculiar pasar por los argumentos. O hacer lo parametrizado en la subclase y perder la información de qué pruebas exactas pasaron / fallaron y solo tienen la última captura de pantalla (ya que los nombres de las capturas de pantalla obtienen el nombre de la prueba en sí). De cualquier manera, fue un poco complicado.

En mi caso, SWTBotJunit4ClassRunner es bastante simple, así que cloné el código fuente de la clase, le di mi propio nombre ParametrizedScreenshotRunner y donde el original estaba extendiendo el TestRunner , mi clase está extendiendo la clase parametrizada , así que en esencia puedo usar mi propio corredor en lugar de los dos anteriores. Resumido, mi propio corredor se extiende sobre el corredor parametrizado mientras implementa la función de captura de pantalla, ahora mi prueba usa este corredor "híbrido" y todas las pruebas funcionan como se esperaba de inmediato (no es necesario cambiar nada dentro de las pruebas).

Así es como se ve (en aras de la brevedad, eliminé todos los comentarios de la lista):

package mySwtTests;

import org.junit.runners.Parameterized;
import org.eclipse.swtbot.swt.finder.junit.ScreenshotCaptureListener;
import org.junit.runner.notification.RunListener;
import org.junit.runner.notification.RunNotifier;

public class ParametrizedScreenshotRunner extends TestRu Parameterized {

    public ParametrizedScreenshotRunner(Class<?> klass) throws Throwable {
        super(klass);
    }

    public void run(RunNotifier notifier) {
        RunListener failureSpy = new ScreenshotCaptureListener();
        notifier.removeListener(failureSpy); // remove existing listeners that could be added by suite or class runners
        notifier.addListener(failureSpy);
        try {
            super.run(notifier);
        } finally {
            notifier.removeListener(failureSpy);
        }
    }
}
muni764
fuente
-15

También puedes probar esto:

@RunWith(JUnitParamsRunner.class)
public class AbstractTestClass {
  // some tests
}

@RunWith(MockitoJUnitRunner.class)
public class DatabaseModelTest extends AbstractTestClass {
  // some tests
}
Valentin Uveges
fuente
2
Esto no funcionará, solo se procesará la anotación de la subclase.
PaulNUK
no funciona - solo se tendrá en cuenta la anotación MockitoJUnitRunner
Przemek Bielicki