Ignorar condicionalmente las pruebas en JUnit 4

365

OK, entonces la @Ignoreanotación es buena para marcar que no se debe ejecutar un caso de prueba.

Sin embargo, a veces quiero ignorar una prueba basada en información de tiempo de ejecución. Un ejemplo podría ser si tengo una prueba de concurrencia que necesita ejecutarse en una máquina con un cierto número de núcleos. Si esta prueba se ejecutara en una máquina de un solo procesador, no creo que sea correcto pasar la prueba (ya que no se ha ejecutado), y ciertamente no sería correcto fallar la prueba y romper la compilación .

Por lo tanto, quiero poder ignorar las pruebas en tiempo de ejecución, ya que este parece ser el resultado correcto (ya que el marco de prueba permitirá que la compilación pase pero registre que las pruebas no se ejecutaron). Estoy bastante seguro de que la anotación no me dará esta flexibilidad, y sospecho que necesitaré crear manualmente el conjunto de pruebas para la clase en cuestión. Sin embargo, la documentación no menciona nada acerca de esto y al mirar a través de la API tampoco está claro cómo se haría esto programáticamente (es decir, ¿cómo creo programáticamente una instancia Testo similar que sea equivalente a la creada por la @Ignoreanotación?).

Si alguien ha hecho algo similar en el pasado, o tiene una idea brillante de cómo podría hacer esto, me alegraría saberlo.

Andrzej Doyle
fuente

Respuestas:

476

La forma JUnit es hacer esto en tiempo de ejecución es org.junit.Assume.

 @Before
 public void beforeMethod() {
     org.junit.Assume.assumeTrue(someCondition());
     // rest of setup.
 }

Puede hacerlo en un @Beforemétodo o en la prueba en sí, pero no en un @Aftermétodo. Si lo hace en la prueba, su @Beforemétodo se ejecutará. También puede hacerlo dentro @BeforeClasspara evitar la inicialización de la clase.

Un error de suposición hace que se ignore la prueba.

Editar: para comparar con la @RunIfanotación de junit-ext , su código de muestra se vería así:

@Test
public void calculateTotalSalary() {
    assumeThat(Database.connect(), is(notNull()));
    //test code below.
}

Sin mencionar que es mucho más fácil capturar y usar la conexión del Database.connect()método de esta manera.

Yishai
fuente
1
@notnoop, esa no es mi observación en absoluto. Son ignorados El corredor de pruebas de IDEA los informa de esa manera, y una mirada al código fuente de JUnit muestra que informa que la prueba se ignora.
Yishai, el
1
Para citar: "En el futuro, esto puede cambiar, y una suposición fallida puede hacer que se ignore la prueba". De hecho, cambió, a partir de 4.5 creo. El javadoc actual dice: "El corredor JUnit predeterminado trata las pruebas con supuestos erróneos como ignorados. Los corredores personalizados pueden comportarse de manera diferente". github.com/KentBeck/junit/blob/…
Yishai
44
Eclipse 3.6 con Junit 4.8.1 informa suposiciones falsas como una prueba de aprobación. Lo mismo con la hormiga 1.8.1.
fijiaaron
8
Que Eclipse informa que las suposiciones fallidas son un error: bugs.eclipse.org/bugs/show_bug.cgi?id=359944
Martin
1
@ JeffStorey, entonces estás buscando un par de cosas. Una es la @BeforeClassanotación, donde puede hacer que su suposición falle allí, lo que saltará toda la clase. Otro es @ClassRule(para el control de grano fino, pero sobre toda la clase, una vez).
Yishai
51

Deberías pagar el Junit-extproyecto. Tienen una RunIfanotación que realiza pruebas condicionales, como:

@Test
@RunIf(DatabaseIsConnected.class)
public void calculateTotalSalary() {
    //your code there
}

class DatabaseIsConnected implements Checker {
   public boolean satisify() {
        return Database.connect() != null;
   }
}

[Ejemplo de código tomado de su tutorial]

notnoop
fuente
3
Gracias por esta respuesta: una sintaxis alternativa interesante para la funcionalidad, aunque la utilizaré Assumedirectamente para no introducir otra dependencia.
Andrzej Doyle
3
Personalmente prefiero esta solución. Si tiene muchas pruebas que deberían ejecutarse en base a las mismas condiciones, esto sería mucho más ideal que tener que usar Asumir en cada prueba. Además, si esto se puede utilizar a nivel de clase en lugar de a nivel de método, será aún más ideal.
Richard
Lo preferiría, porque esto ayuda a ejecutar la prueba condicionalmente en tiempo de ejecución. Se adapta a donde se ejecutarán varias pruebas unitarias y el requisito es ejecutar las pruebas unitarias en un verificador particular. Realmente me sorprendió ver que junit-ext no está disponible en el repositorio maven. ¿Cómo podríamos aprovechar esto en el proyecto Maven?
shambhu
44
Una anotación como @RunIfsepara la condición en la que se debe ejecutar una prueba del código de prueba real, lo que creo que es bueno. Lo que no me gusta es que requiere un corredor de prueba en particular. Por lo tanto, escribí una regla JUnit para ignorar las pruebas.
Rüdiger Herrmann
2
Después de instalar junit-ext jar (que se encuentra aquí code.google.com/p/junit-ext/downloads/… ) en nuestro repositorio local e implementar esta anotación @RunIf ... ¡nada! Se ignora por completo, y creo que la razón podría ser que junit-ext parece depender de junit 4.5. Necesitamos 4.9+ debido a la prueba de primavera. Entonces ... no importa eso.
Marc
7

En JUnit 4, otra opción para usted puede ser crear una anotación para indicar que la prueba debe cumplir con sus criterios personalizados, luego extender el corredor predeterminado con los suyos y, utilizando la reflexión, basar su decisión en los criterios personalizados. Puede verse más o menos así:

public class CustomRunner extends BlockJUnit4ClassRunner {
    public CTRunner(Class<?> klass) throws initializationError {
        super(klass);
    }

    @Override
    protected boolean isIgnored(FrameworkMethod child) {
        if(shouldIgnore()) {
            return true;
        }
        return super.isIgnored(child);
    }

    private boolean shouldIgnore(class) {
        /* some custom criteria */
    }
}
Kyle Shrader
fuente
Si bien esto se ve bien y limpio, no funciona con las versiones actuales de JUnit4, ya que BlockJUnit4ClassRunnerya no ofrece el isIgnoredmétodo.
Dave
-2

Una nota rápida: Assume.assumeTrue(condition)ignora el resto de los pasos pero pasa la prueba. Para fallar la prueba, use org.junit.Assert.fail()dentro de la declaración condicional. Funciona igual que Assume.assumeTrue()pero falla la prueba.

TIn TIn
fuente
55
Como se señaló en las respuestas anteriores, una suposición fallida no hace que la prueba pase, devuelve un estado separado. Algunos corredores pueden informar erróneamente esto como si fuera un pase, pero eso es una debilidad / error en el corredor de prueba (y el corredor JUnit predeterminado muestra la prueba como ignorada). Y en cuanto a tu oración final, reprobar la prueba no es específicamente lo que quiero (ed) hacer.
Andrzej Doyle
Oh ok Las pruebas superaron la suposición fallida en mi caso, pero quería que se informaran como fallidas (estaba buscando una excepción de Test Watcher). Forzar un fracaso me ayudó.
TIn TIn