JUnit4 fail () está aquí, pero ¿dónde está pass ()?

82

Hay un fail()método en la biblioteca JUnit4. Me gusta, pero experimento una falta de pass()método que no está presente en la biblioteca. ¿Por que es esto entonces?

Descubrí que puedo usarlo assertTrue(true)en su lugar, pero aún parece poco lógico.

@Test
 public void testSetterForeignWord(){
  try {
   card.setForeignWord("");
   fail();
  } catch (IncorrectArgumentForSetter ex){
  }

 // assertTrue(true);
 }
Eugenio
fuente
7
Simplemente use la declaración de retorno, en la mayoría de los casos pasará como pass ().
topchef
@topchef ese único comentario golpeó el martillo en la cabeza, mientras todos los demás debaten sobre qué es aceptable y cuál no.
Algunos sistemas de prueba (perl Test :: Simple) cuentan las afirmaciones pasadas y fallidas. Junit, sin embargo, cuenta la cantidad de métodos de prueba que pasan y fallan. Por tanto, Junit no tiene el mismo uso para un passmétodo.
Randall Whitman

Respuestas:

63

Siempre que la prueba no arroje una excepción, pasa, a menos que su @Testanotación especifique una excepción esperada. Supongo que pass()podría lanzar una excepción especial que JUnit siempre interpreta como aprobada, para cortocircuitar la prueba, pero eso iría en contra del diseño habitual de las pruebas (es decir, asumir el éxito y solo fallar si falla una afirmación) y, si la gente obtiene la idea de que era preferible usarlo pass(), ralentizaría significativamente un gran conjunto de pruebas aprobadas (debido a la sobrecarga de creación de excepciones). Reprobar las pruebas no debería ser la norma, por lo que no es gran cosa si tienen esa sobrecarga.

Tenga en cuenta que su ejemplo podría reescribirse así:

@Test(expected=IncorrectArgumentForSetter.class)
public void testSetterForeignWord("") throws Exception {
  card.setForeignWord("");
}

Además, debería favorecer el uso de excepciones estándar de Java. Su IncorrectArgumentForSetterprobablemente debería haber una IllegalArgumentException.

ColinD
fuente
4
El método fail () y los métodos assertX () realmente solo arrojan un AssertionError, lo que hace que el método de prueba se cierre de forma incorrecta. Es por eso que un regreso exitoso indica éxito ...
Steven Schlansker
-1 - el método pass () no está haciendo nada, está saliendo de la prueba inmediatamente sin excepciones. Eso sería efectivamente equivalente a la declaración de retorno en la mayoría de los casos (excepto en las excepciones esperadas, tiempos de espera, etc.).
topchef
1
@grigory: Tienes razón en que un pass()método no puede simplemente no hacer nada, no sea que sea posible que una prueba falle después de llamarlo. Así que eliminé esa oración.
ColinD
No puedo agradecerles lo suficiente, ¡me ayudaron a salir de mucha frustración!
Programador N00b
70

Llame a la returndeclaración cada vez que su prueba haya terminado y aprobado.

Horrocrux7
fuente
3
+1 debe haber sido la respuesta correcta (en la mayoría de los casos con la excepción de los tiempos de espera esperados, etc.)
topchef
De hecho, parece la forma más sencilla y útil de finalizar una prueba.
Benj
7

Creo que esta pregunta necesita una respuesta actualizada, ya que la mayoría de las respuestas aquí están bastante desactualizadas.

En primer lugar a la pregunta del OP:

Creo que está bastante bien aceptado que introducir el concepto de "superación esperada" en JUnit fue un mal movimiento, ya que esa excepción podría plantearse en cualquier lugar y pasará la prueba. Funciona si está lanzando (y afirmando) excepciones muy específicas de dominio, pero solo lanzo ese tipo de excepciones cuando estoy trabajando en un código que debe ser absolutamente impecable, la mayoría de APIS simplemente arrojará las excepciones integradas como IllegalArgumentExceptiono IllegalStateException. Si dos llamadas que hagas podrían arrojar potencialmente estas excepciones, entonces la @ExpectedExceptionanotación pondrá una barra verde en tu prueba, incluso si es la línea incorrecta la que arroja la excepción.

Para esta situación, escribí una clase que estoy seguro de que muchos otros aquí han escrito, ese es un assertThrowsmétodo:

public class Exceptions {
    private Exceptions(){}

    public static void assertThrows(Class<? extends Exception> expectedException, Runnable actionThatShouldThrow){
        try{
            actionThatShouldThrow.run();
            fail("expected action to throw " + expectedException.getSimpleName() + " but it did not.");
        }
        catch(Exception e){
            if ( ! expectedException.isInstance(e)) {
                throw e;
            }
        }
    }
}

este método simplemente regresa si se lanza la excepción, lo que le permite hacer más afirmaciones / verificaciones en su prueba.

con la sintaxis de Java 8, su prueba se ve muy bien. A continuación se muestra una de las pruebas más simples en nuestro modelo que usa el método:

@Test
public void when_input_lower_bound_is_greater_than_upper_bound_axis_should_throw_illegal_arg() {
    //setup
    AxisRange range = new AxisRange(0,100);

    //act
    Runnable act = () -> range.setLowerBound(200);

    //assert
    assertThrows(IllegalArgumentException.class, act);
}

estas pruebas son un poco inestables porque el paso "actuar" en realidad no realiza ninguna acción, pero creo que el significado sigue siendo bastante claro.

También hay una pequeña biblioteca en maven llamada catch-exception que usa la sintaxis de estilo mockito para verificar que se lanzan excepciones. Se ve bonito, pero no soy fanático de los proxies dinámicos. Dicho esto, la sintaxis es tan elegante que sigue siendo tentadora:

// given: an empty list
List myList = new ArrayList();

// when: we try to get the first element of the list
// then: catch the exception if any is thrown 
catchException(myList).get(1);

// then: we expect an IndexOutOfBoundsException
assert caughtException() instanceof IndexOutOfBoundsException;

Por último, para la situación con la que me encontré para llegar a este hilo, hay una manera de ignorar las pruebas si se cumple alguna condición.

En este momento estoy trabajando para que se llamen algunas DLL a través de una biblioteca de carga de biblioteca nativa de Java llamada JNA, pero nuestro servidor de compilación está en ubuntu. Me gusta intentar impulsar este tipo de desarrollo con las pruebas JUnit, aunque en este momento están lejos de ser "unidades". Lo que quiero hacer es ejecutar la prueba si estoy en una máquina local, pero ignorar la prueba si estamos en ubuntu. JUnit 4 tiene una provisión para esto, llamada Assume:

@Test
public void when_asking_JNA_to_load_a_dll() throws URISyntaxException {
    //this line will cause the test to be branded as "ignored" when "isCircleCI" 
    //(the machine running ubuntu is running this test) is true.
    Assume.assumeFalse(BootstrappingUtilities.isCircleCI());
    //an ignored test will typically result in some qualifier being put on the results, 
    //but will also not typically prevent a green-ton most platforms. 

    //setup
    URL url = DLLTestFixture.class.getResource("USERDLL.dll");
    String path = url.toURI().getPath();
    path = path.substring(0, path.lastIndexOf("/"));

    //act
    NativeLibrary.addSearchPath("USERDLL", path);
    Object dll = Native.loadLibrary("USERDLL", NativeCallbacks.EmptyInterface.class);

    //assert
    assertThat(dll).isNotNull();
}
Groostav
fuente
Como dijiste, "actuar" no hace nada específico, por lo que todavía no sabemos qué produce exactamente la excepción, ya sea que sea la línea que esperamos o no. En este caso, el ejemplo es bastante simple de probar y verificar, pero imagine que su prueba incluye el método de utilidad que usa métodos anidados antes de devolver el resultado. Todavía no hay forma de obtener la fuente del problema a menos que IDE indique el sitio de excepción
Farid
4

También estaba buscando un passmétodo para JUnit, de modo que pudiera cortocircuitar algunas pruebas que no eran aplicables en algunos escenarios (hay pruebas de integración, en lugar de pruebas unitarias puras). Lástima que no esté allí.

Afortunadamente, hay una manera de ignorar una prueba condicionalmente, que en realidad se ajusta aún mejor en mi caso usando el assumeTruemétodo:

Assume.assumeTrue (isTestApplicable);

Entonces, aquí la prueba se ejecutará solo si isTestApplicable es verdadero; de lo contrario, se ignorará la prueba.

Sebastián K
fuente
2

No es necesario el método de paso porque cuando no se lanza ninguna AssertionFailedException desde el código de prueba, el caso de prueba unitario pasará.

El método fail () en realidad arroja una AssertionFailedException para fallar el testCase si el control llega a ese punto.

Ajay
fuente
Creo que en realidad es un junit.framework.AssertionFailedError.
Kyle
¿Qué pasa con los casos de errores? Diga iam obteniendo el elemento no visible, excepción de webdriver. ¿Debería capturar la excepción y regresar?
sasikumar
1

Creo que esta pregunta es el resultado de un pequeño malentendido del proceso de ejecución de la prueba. En JUnit (y otras herramientas de prueba) los resultados se cuentan por método, no por llamada de aserción. No hay un contador, que realiza un seguimiento de cuántos aprobados / fallidos assertXse ejecutaron.

JUnit ejecuta cada método de prueba por separado. Si el método devuelve correctamente, la prueba se registró como "aprobada". Si se produce una excepción, la prueba se registró como "fallida". En el último caso, son posibles dos subcasos: 1) una excepción de aserción JUnit, 2) cualquier otro tipo de excepciones. El estado será "fallido" en el primer caso y "error" en el segundo caso.

En la Assertclase se encuentran disponibles muchos métodos abreviados para lanzar excepciones de aserción. En otras palabras, Assertes una capa de abstracción sobre las excepciones de JUnit.

Por ejemplo, este es el código fuente de assertEqualsen GitHub :

/**
 * Asserts that two Strings are equal.
 */
static public void assertEquals(String message, String expected, String actual) {
    if (expected == null && actual == null) {
        return;
    }
    if (expected != null && expected.equals(actual)) {
        return;
    }
    String cleanMessage = message == null ? "" : message;
    throw new ComparisonFailure(cleanMessage, expected, actual);
}

Como puede ver, en caso de igualdad no pasa nada, de lo contrario se lanzará una excepción.

Entonces:

assertEqual("Oh!", "Some string", "Another string!");

simplemente lanza una ComparisonFailureexcepción, que será detectada por JUnit, y

assertEqual("Oh?", "Same string", "Same string");

no hace nada.

En resumen, algo como pass()no tendría ningún sentido, porque no hizo nada.

Dávid Horváth
fuente