JUnit confusion: use 'extend TestCase' o '@Test'?

152

El uso adecuado (o al menos la documentación) de JUnit me pareció muy confuso. Esta pregunta sirve tanto como referencia futura como como una pregunta real.

Si he entendido correctamente, hay dos enfoques principales para crear y ejecutar una prueba JUnit:

Enfoque A (estilo JUnit 3): cree una clase que amplíe TestCase y comience los métodos de prueba con la palabra test. Al ejecutar la clase como una prueba JUnit (en Eclipse), todos los métodos que comienzan con la palabra testse ejecutan automáticamente.

import junit.framework.TestCase;

public class DummyTestA extends TestCase {

    public void testSum() {
        int a = 5;
        int b = 10;
        int result = a + b;
        assertEquals(15, result);
    }
}

Enfoque B (estilo JUnit 4): cree una clase 'normal' y anteponga una @Testanotación al método. Tenga en cuenta que NO tiene que comenzar el método con la palabra test.

import org.junit.*;
import static org.junit.Assert.*;

public class DummyTestB {

    @Test
    public void Sum() {
        int a = 5;
        int b = 10;
        int result = a + b;
        assertEquals(15, result);
    }
}

Mezclar los dos parece no ser una buena idea, vea, por ejemplo, esta pregunta de stackoverflow :

Ahora, mis preguntas:

  1. ¿Cuál es el enfoque preferido , o cuándo usaría uno en lugar del otro?
  2. El enfoque B permite probar las excepciones extendiendo la anotación @Test como en @Test(expected = ArithmeticException.class). Pero, ¿cómo se prueban las excepciones cuando se utiliza el enfoque A?
  3. Al utilizar el enfoque A, puede agrupar varias clases de prueba en un conjunto de pruebas como esta:

    TestSuite suite = new TestSuite("All tests");
    suite.addTestSuite(DummyTestA.class);
    suite.addTestSuite(DummyTestAbis.class);

    Pero esto no se puede usar con el enfoque B (ya que cada clase de prueba debería subclasificar TestCase). ¿Cuál es la forma correcta de agrupar las pruebas para el enfoque B?

Editar: he agregado las versiones JUnit a ambos enfoques

Rabarberski
fuente
He visto extends TestCasey luego cada prueba también anotó @Testsolo para confundir las cosas. :)
EM-Creations

Respuestas:

119

La distinción es bastante fácil:

  • extender TestCasees la forma en que se escribieron las pruebas unitarias en JUnit 3 (por supuesto, todavía es compatible con JUnit 4)
  • El uso de la @Testanotación es la forma introducida por JUnit 4

En general, debe elegir la ruta de anotación, a menos que se necesite compatibilidad con JUnit 3 (y / o una versión de Java anterior a Java 5). La nueva forma tiene varias ventajas:

Para probar las excepciones esperadas en un JUnit 3 TestCase, debe hacer que el texto sea explícito.

public void testMyException() {
  try {
    objectUnderTest.myMethod(EVIL_ARGUMENT);
    fail("myMethod did not throw an Exception!");
  } catch (MyException e) {
    // ok!
    // check for properties of exception here, if desired
  }
}

JUnit 5 introdujo otro cambio de API, pero aún usa anotaciones. La nueva @Testanotación es org.junit.jupiter.api.Test(la antigua JUnit 4 era org.junit.Test), pero funciona más o menos igual que la JUnit 4.

Joachim Sauer
fuente
Respuesta útil y exhaustiva, pero no entiendo completamente "buscar mensaje de excepción". Comprobar con una cadena codificada será una pesadilla de mantenimiento. Debe haber significado "verificar las propiedades de su tipo de excepción específico".
thSoft
3
@thSoft: no es frecuente que se use, pero ocasionalmente quiero asegurarme de que el método de excepción mencione el campo ofensivo, por ejemplo. Entonces un simple assertTrue(e.getMessage().contains("foo"))podría ser útil.
Joachim Sauer
1
Incluso en JUnit4 este es un modismo importante cuando tiene que verificar el mensaje o alguna otra propiedad de la excepción (como la causa). El expectedmétodo solo verifica el tipo.
Yishai
@Yishai: eso es cierto, pero la mayoría de las veces ya estoy contento si el método arroja el tipo correcto de excepción en la entrada problemática.
Joachim Sauer
Por esa razón, JUnit 5 realizó un cambio radical en las pruebas de excepción. ClaimThrows () es fantástico :-)
Marcus K.
25

Prefiero JUnit 4 (enfoque de anotación) porque lo encuentro más flexible.

Si desea crear un conjunto de pruebas en JUnit 4, debe crear una clase que agrupe todas las pruebas de esta manera:

import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;


@RunWith(Suite.class)
@SuiteClasses({
    Test1.class,
    Test2.class,
    Test3.class,
    Test4.class
})public class TestSuite
{
 /* empty class */
}
Mugriento
fuente
15

Hay una parte sin respuesta a su pregunta, y es "¿Cuál es la forma correcta de agrupar las pruebas para el enfoque B?"

La respuesta oficial es que anota una clase con un @RunWith (Suite.class) y luego usa la anotación @ Suite.SuiteClasses para enumerar las clases. Así es como lo hacen los desarrolladores de JUnit (enumerando cada clase en una suite de forma manual). En muchos sentidos, este enfoque es una mejora, ya que es trivial e intuitivo agregar comportamientos antes y después de la suite (solo agregue un método @BeforeClass y @AfterClass a la clase anotada con @RunWith, mucho mejor que el antiguo TestFixture )

Sin embargo, tiene un paso atrás, ya que las anotaciones no le permiten crear dinámicamente la lista de clases, y solucionar ese problema se vuelve un poco feo. Debe subclasificar la clase Suite y crear dinámicamente la matriz de clases en la subclase y pasarla al constructor Suite, pero esta es una solución incompleta en que otras subclases de Suite (como Categorías) no funcionan con ella y esencialmente no es compatible con la colección de clases de prueba dinámica.

Yishai
fuente
1
+1 por esto. Después de embarcarme en una tarea para escribir una solución dinámica para agregar pruebas a un TestSuite, tuve que extender TestCase en cada una de mis pruebas. Esto, a su vez, ha roto las pruebas de unidades que funcionaban anteriormente, que usaban anotaciones JUnit4 para definir las excepciones esperadas. Mi búsqueda de una forma de poblar dinámicamente un Test Suite me ha llevado a este hilo, y específicamente a su respuesta, que creo que es una de las pocas razones deseables restantes para continuar con JUnit 3.
8bitjunkie
4

Deberías usar JUnit 4. Es mejor.

Muchos marcos han comenzado a desaprobar el soporte de JUnit 3.8.

Esto es de la documentación de referencia de Spring 3.0:

[Advertencia] La jerarquía de clase JUnit 3.8 heredada está en desuso

En general, siempre debe intentar usar la última versión estable de un marco cuando comience algo nuevo.

Espen
fuente
1
  1. El enfoque "preferido" sería usar anotaciones que se han introducido desde el 4 de junio. Facilitan mucho las cosas (vea su segunda pregunta)

  2. Puede usar un simple bloque try / catch para eso:


public void testForException() {
    try {
        Integer.parseInt("just a string");
        fail("Exception should have been thrown");
    } catch (final Exception e) {
        // expected
    }
}
black666
fuente