¿Cómo ejecutar métodos de prueba en orden específico en JUnit4?

413

Quiero ejecutar métodos de prueba que están anotados @Testen un orden específico.

Por ejemplo:

public class MyTest {
    @Test public void test1(){}
    @Test public void test2(){}
}

Quiero asegurarme de ejecutar test1()antes de test2()cada vez que corro MyTest, pero no pude encontrar anotaciones como @Test(order=xx).

Creo que es una función bastante importante para JUnit, si el autor de JUnit no quiere la función de pedido , ¿por qué?

卢 声 远 Shengyuan Lu
fuente
2
Me parecen ejecutados en el orden en que aparecen en el archivo fuente.
Marqués de Lorne
84
Nunca debe escribir pruebas que deban ejecutarse en un orden específico. Esa es realmente una mala práctica. Cada prueba debe poder ejecutarse de forma independiente.
Apfelsaft
55
@EJP esto era casi universalmente cierto para java pre 7. Pre 7, la mayoría de las JVM hicieron esto, pero nunca estuvo garantizado. Las JVM de Java 7 pueden devolver los métodos en un orden no determinista.
Matthew Farwell el
17
Solución alterna. Elimine @Test de sus casos de prueba, conviértalos como funciones privadas, luego cree un solo caso de prueba y llame a las funciones privadas en orden.
Simon Guo
12
Eliminar @Test de los casos de prueba arruinará el informe JUnit. Por cierto, hacer cumplir un orden específico es una mala práctica para las pruebas unitarias, pero no necesariamente una mala práctica para las pruebas de integración . La mejor opción (no ideal) es para anotar la clase con @FixMethodOrder(MethodSorters.NAME_ASCENDING), mantenga la @Testanotación para todos los métodos de prueba y cambiar el nombre alfabéticamente según el orden deseado de ejecución, por ejemplo t1_firstTest(), t2_secondTest(), etc.
MisterStrickland

Respuestas:

238

Creo que es una función bastante importante para JUnit, si el autor de JUnit no quiere la función de pedido, ¿por qué?

No estoy seguro de que haya una manera limpia de hacer esto con JUnit, que yo sepa, JUnit asume que todas las pruebas se pueden realizar en un orden arbitrario. De las preguntas frecuentes:

¿Cómo uso un dispositivo de prueba?

(...) No se garantiza el orden de las invocaciones del método de prueba , por lo que testOneItemCollection () podría ejecutarse antes de testEmptyCollection (). (...)

¿Por que es esto entonces? Bueno, creo que hacer que las pruebas dependan del orden es una práctica que los autores no quieren promover. Las pruebas deben ser independientes, no deben estar acopladas y violar esto hará que las cosas sean más difíciles de mantener, romperá la capacidad de ejecutar pruebas individualmente (obviamente), etc.

Dicho esto, si realmente quiere ir en esta dirección, considere usar TestNG ya que admite la ejecución de métodos de prueba en cualquier orden arbitrario de forma nativa (y cosas como especificar que los métodos dependen de grupos de métodos). Cedric Beust explica cómo hacer esto en orden de ejecución de pruebas en testng .

Pascal Thivent
fuente
14
O tiene dos pruebas independientes, o solo tiene una prueba y debe codificar como tal.
Jon Freedman
2
@JonFreedman, como entiendo la pregunta, no se trata de que las pruebas sean interdependientes, solo de tener una especificación de cosas para probar y querer que los resultados aparezcan en ese orden.
Jon Bright
174
Puedo entender que no se impone el orden para las pruebas unitarias, sin embargo, cuando se usa JUnit para escribir pruebas de integración, sería bueno poder especificar el orden en que se ejecutan las pruebas. Ej. Ejecute la prueba de inicio de sesión primero.
Brian DiCasa
13
@BrianD. el inicio de sesión es probablemente un "accesorio" en lugar de una prueba que debe ejecutarse antes que todos los demás. Probablemente escribiré un BeforeClass que inicie sesión y luego escribiré las pruebas para ejecutarlas en cualquier orden.
marcospereira
47
La implicación "las pruebas deben ser independientes => las pruebas deben ser independientes del PEDIDO" no es cierta. Considere la calificación automatizada de las tareas de los estudiantes. Quiero probar su solución para entradas más pequeñas primero y para entradas más grandes después. Cuando la solución falla para entradas más pequeñas (para límite de tiempo / memoria), ¿por qué deberían ejecutarse las pruebas para entradas más grandes?
mirelon
96

Si se deshace de su instancia existente de Junit y descarga JUnit 4.11 o superior en la ruta de compilación, el siguiente código ejecutará los métodos de prueba en el orden de sus nombres, ordenados en orden ascendente:

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class SampleTest {

    @Test
    public void testAcreate() {
        System.out.println("first");
    }
    @Test
    public void testBupdate() {
        System.out.println("second");
    }
    @Test
    public void testCdelete() {
        System.out.println("third");
    }
}
Aniket Kulkarni
fuente
8
De hecho, hemos probado un método similar test_001_login (), por ejemplo, pero aunque funciona principalmente para preservar el orden, no está garantizado, tenemos varias instancias por ejecución de prueba donde 004, 005 y 006 se ejecutan después de 007. Hace que dices "WTF!" y corres a StackOverflow para obtener respuestas.
Max P Magee
Impresionante - disponible en JUnit 4.12
DtechNet
1
en mis pruebas: testAcase - funcionó, test_A_case / testA_case - ¡no!
Rodislav Moldovan
66
He probado este parámetro de anotación "MethodSorters.JVM", por ejemplo, "@FixMethodOrder (MethodSorters.JVM)". Desde la API: JVM: deja los métodos de prueba en el orden devuelto por la JVM. Funciona bien para lo que estoy haciendo (CRUD), ejecuta los métodos de prueba en el orden en que están escritos. +1
Edvinauskas
1
Esta anotación es de hecho una respuesta, pero tiene la advertencia de que no está definida (en Junit 4.12) @Inheritedy, por lo tanto , deja de ser efectiva en mi AbstractTestCaseclase principal.
AbVog
49

Si el pedido es importante, debe hacerlo usted mismo.

@Test public void test1() { ... }
@Test public void test2() { test1(); ... }

En particular, debe enumerar algunas o todas las posibles permutaciones de pedido para probar, si es necesario.

Por ejemplo,

void test1(); 
void test2(); 
void test3(); 


@Test
public void testOrder1() { test1(); test3(); }

@Test(expected = Exception.class)
public void testOrder2() { test2(); test3(); test1(); }

@Test(expected = NullPointerException.class)
public void testOrder3() { test3(); test1(); test2(); }

O una prueba completa de todas las permutaciones:

@Test
public void testAllOrders() {
    for (Object[] sample: permute(1, 2, 3)) {
        for (Object index: sample) {
            switch (((Integer) index).intValue()) {
                case 1: test1(); break; 
                case 2: test2(); break; 
                case 3: test3(); break; 
            }
        }
    }
}

Aquí, permute()es una función simple que itera todas las permutaciones posibles en una Colección de matriz.

Xiè Jìléi
fuente
Pero, ¿y si las pruebas en diferentes archivos?
Oleg Abrazhaev
3
En el primer bloque de código, se test2ejecuta test1 nuevamente . Junit todavía puede correr test2antes test1. Es probable que esto no sea lo que pretendía, y no es una respuesta válida a la pregunta.
Toolforger
47

La migración a TestNG parece la mejor manera, pero no veo una solución clara aquí para jUnit. Aquí está la solución / formato más legible que encontré para jUnit:

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class SampleTest {
    @Test
    void stage1_prepareAndTest(){};

    @Test
    void stage2_checkSomething(){};

    @Test
    void stage2_checkSomethingElse(){};

    @Test
    void stage3_thisDependsOnStage2(){};

    @Test
    void callTimeDoesntMatter(){}
}

Esto garantiza que los métodos de etapa2 se invoquen después de los de etapa1 y antes de los de etapa3.

joro
fuente
55
Este enfoque es bueno, pero sería válido mencionar que si tiene más de 10 pruebas, no funcionará bien a menos que agregue un 0prefijo, por ejemplovoid stage01_prepareAndTest(){ }
EliuX
Si tiene más de 10 etapas (no pruebas) : Sí. Prefiero limitar el número de etapas y tener más pruebas en cada etapa, cuando esto es posible.
joro
18

Es uno de los principales problemas que enfrenté cuando trabajé en Junit y se me ocurrió la siguiente solución que funciona bien para mí:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;

public class OrderedRunner extends BlockJUnit4ClassRunner {

    public OrderedRunner(Class<?> clazz) throws InitializationError {
        super(clazz);
    }

    @Override
    protected List<FrameworkMethod> computeTestMethods() {
        List<FrameworkMethod> list = super.computeTestMethods();
        List<FrameworkMethod> copy = new ArrayList<FrameworkMethod>(list);
        Collections.sort(copy, new Comparator<FrameworkMethod>() {

            @Override
            public int compare(FrameworkMethod f1, FrameworkMethod f2) {
                Order o1 = f1.getAnnotation(Order.class);
                Order o2 = f2.getAnnotation(Order.class);

                if (o1 == null || o2 == null) {
                    return -1;
                }

                return o1.order() - o2.order();
            }
        });
        return copy;
    }
}

También cree una interfaz como la siguiente:

 @Retention(RetentionPolicy.RUNTIME)


@Target({ ElementType.METHOD})

public @interface Order {
public int order();
}

Ahora suponga que tiene clase A donde ha escrito varios casos de prueba como a continuación:

(@runWith=OrderRunner.class)
Class A{
@Test
@Order(order = 1)

void method(){

//do something

}

}

Entonces la ejecución comenzará desde el método llamado "método ()". ¡Gracias!

Aman Goel
fuente
Uso de otro JUnit Runner con PowerMock Desde la versión 1.6.0, PowerMock tiene soporte para delegar la ejecución de la prueba a otro JUnit runner sin usar una Regla JUnit. Esto deja la ejecución de prueba real a otro corredor de su elección. @RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(OrderedRunner.class)
Kyriakos Georgiopoulos
11

JUnit actualmente permite que los métodos de prueba ejecuten el orden utilizando anotaciones de clase:

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FixMethodOrder(MethodSorters.JVM)
@FixMethodOrder(MethodSorters.DEFAULT)

Por defecto, los métodos de prueba se ejecutan en orden alfabético. Entonces, para establecer el orden de métodos específicos, puede nombrarlos como:

a_TestWorkUnit_WithCertainState_ShouldDoSomething b_TestWorkUnit_WithCertainState_ShouldDoSomething c_TestWorkUnit_WithCertainState_ShouldDoSomething

Puedes encontrar ejemplos aquí .

Zon
fuente
6

Mira un informe de JUnit. JUnit ya está organizado por paquete. Cada paquete tiene (o puede tener) clases TestSuite, cada una de las cuales ejecuta múltiples TestCases. Cada TestCase puede tener múltiples métodos de prueba del formulario public void test*(), cada uno de los cuales se convertirá en una instancia de la clase TestCase a la que pertenecen. Cada método de prueba (instancia de TestCase) tiene un nombre y un criterio de aprobación / falla.

Lo que mi administración requiere es el concepto de elementos individuales de TestStep , cada uno de los cuales informa sus propios criterios de aprobación / reprobación . El fracaso de cualquier paso de prueba no debe impedir la ejecución de los pasos de prueba posteriores.

En el pasado, los desarrolladores de pruebas en mi posición organizaron las clases TestCase en paquetes que corresponden a la (s) parte (s) del producto bajo prueba, crearon una clase TestCase para cada prueba e hicieron de cada método de prueba un "paso" separado en la prueba, completo con sus propios criterios de pasa / falla en la salida JUnit. Cada TestCase es una "prueba" independiente, pero los métodos individuales, o "pasos" de prueba dentro de la TestCase, deben ocurrir en un orden específico.

Los métodos de TestCase fueron los pasos de TestCase, y los diseñadores de prueba obtuvieron un criterio de aprobación / falla por paso de prueba. Ahora los pasos de la prueba están mezclados y las pruebas (por supuesto) fallan.

Por ejemplo:

Class testStateChanges extends TestCase

public void testCreateObjectPlacesTheObjectInStateA()
public void testTransitionToStateBAndValidateStateB()
public void testTransitionToStateCAndValidateStateC()
public void testTryToDeleteObjectinStateCAndValidateObjectStillExists()
public void testTransitionToStateAAndValidateStateA()
public void testDeleteObjectInStateAAndObjectDoesNotExist()
public void cleanupIfAnythingWentWrong()

Cada método de prueba afirma e informa sus propios criterios de aprobación / falla por separado. Al colapsar esto en "un gran método de prueba" por el simple hecho de ordenar, se pierde la granularidad de criterios de aprobación / reprobación de cada "paso" en el informe resumido de JUnit. ... y eso molesta a mis gerentes. Actualmente están exigiendo otra alternativa.

¿Alguien puede explicar cómo un JUnit con pedidos de métodos de prueba codificados admitiría criterios de aprobación / falla separados de cada paso de prueba secuencial, como se ejemplificó anteriormente y es requerido por mi administración?

Independientemente de la documentación, veo esto como una seria regresión en el marco de JUnit que dificulta la vida de muchos desarrolladores de pruebas.

Desarrollador anónimo
fuente
6

Actualización de JUnit 5 (y mi opinión)

Creo que es una función bastante importante para JUnit, si el autor de JUnit no quiere la función de pedido, ¿por qué?

Por defecto, las bibliotecas de pruebas unitarias no intentan ejecutar pruebas en el orden que ocurre en el archivo fuente.
JUnit 5 como JUnit 4 funcionan de esa manera. Por qué ? Porque si el orden importa, significa que algunas pruebas están acopladas entre ellas y eso no es deseable para las pruebas unitarias .
Entonces, la @Nestedcaracterística introducida por JUnit 5 sigue el mismo enfoque predeterminado.

Pero para las pruebas de integración, el orden del método de prueba puede ser importante ya que un método de prueba puede cambiar el estado de la aplicación de la forma esperada por otro método de prueba. Por ejemplo, cuando escribe una prueba de integración para un proceso de pago de la tienda electrónica, el primer método de prueba que se ejecutará es registrar un cliente, el segundo es agregar artículos en la cesta y el último es hacer el pago. Si el corredor de prueba no respeta ese orden, el escenario de prueba es defectuoso y fallará.
Por lo tanto, en JUnit 5 (de la versión 5.4) tiene la misma posibilidad de establecer el orden de ejecución anotando la clase de prueba con @TestMethodOrder(OrderAnnotation.class)y especificando el orden con @Order(numericOrderValue)para los métodos que el orden importa.

Por ejemplo :

@TestMethodOrder(OrderAnnotation.class) 
class FooTest {

    @Order(3)
    @Test
    void checkoutOrder() {
        System.out.println("checkoutOrder");
    }

    @Order(2)
    @Test
    void addItemsInBasket() {
        System.out.println("addItemsInBasket");
    }

    @Order(1)
    @Test
    void createUserAndLogin() {
        System.out.println("createUserAndLogin");
    }
}

Salida:

createUserAndLogin

addItemsInBasket

pedido

Por cierto, la especificación @TestMethodOrder(OrderAnnotation.class)parece no ser necesaria (al menos en la versión 5.4.0 que probé).

Nota al margen
Sobre la pregunta: ¿JUnit 5 es la mejor opción para escribir pruebas de integración? No creo que deba ser la primera herramienta a considerar (Cucumber y compañía a menudo pueden aportar valores y características más específicas para las pruebas de integración), pero en algunos casos de prueba de integración, el marco JUnit es suficiente. Esa es una buena noticia de que la función existe.

davidxxx
fuente
4

No estoy seguro de estar de acuerdo, si quiero probar 'Carga de archivos' y luego probar 'Datos insertados por carga de archivos', ¿por qué no querría que estos sean independientes entre sí? Perfectamente razonable, creo que poder ejecutarlos por separado en lugar de tener ambos en un caso de prueba de Goliat.

Mattk
fuente
3

Lo que desea es perfectamente razonable cuando los casos de prueba se ejecutan como una suite.

Desafortunadamente, no hay tiempo para dar una solución completa en este momento, pero eche un vistazo a la clase:

org.junit.runners.Suite

Lo que le permite llamar a casos de prueba (desde cualquier clase de prueba) en un orden específico.

Estos pueden usarse para crear pruebas funcionales, de integración o de sistema.

Esto deja las pruebas unitarias como están sin un orden específico (como se recomienda), ya sea que las ejecute así o no, y luego reutilice las pruebas como parte de una imagen más grande.

Reutilizamos / heredamos el mismo código para la unidad, la integración y las pruebas del sistema, a veces basadas en datos, a veces confirmadas, y otras ejecutadas como un conjunto.

CharlieS
fuente
2
¿No has tenido tiempo de completar esta respuesta desde 2014? ;)
Charlie
2

Vea mi solución aquí: "Junit y Java 7."

En este artículo describo cómo ejecutar las pruebas junit en orden, "como en su código fuente". Las pruebas se ejecutarán, en orden a medida que sus métodos de prueba aparezcan en el archivo de clase.

http://intellijava.blogspot.com/2012/05/junit-and-java-7.html

Pero como dijo Pascal Thivent, esta no es una buena práctica.

kornero
fuente
Había visto tu publicación de blog (¡en ruso!), Pero esto es demasiado complicado.
Nicolas Barbulesco
1
@NicolasBarbulesco Tengo dos blogs (rus y eng). Es demasiado complicado porque no debe crear pruebas con dependencia de orden de ejecución. Mi solución es una solución alternativa, pero la solución real es eliminar esa dependencia.
kornero
1
Esta publicación no contiene una respuesta real. Por favor, considere agregar al menos la explicación básica, además del enlace.
configuración regional predeterminada
0

He leído algunas respuestas y estoy de acuerdo en que no es la mejor práctica, sino la forma más fácil de ordenar sus pruebas, y la forma en que JUnit ejecuta las pruebas de forma predeterminada es por nombre alfabético ascendente.

Así que solo nombra tus pruebas en el orden alfabético que quieras. También tenga en cuenta que el nombre de la prueba debe comenzar con la palabra prueba. Solo ten cuidado con los números

test12 se ejecutará antes de test2

entonces:

testA_MyFirstTest testC_ThirdTest testB_ATestThatRunsSecond

pstorli
fuente
0

Consulte este: https://github.com/TransparentMarket/junit . Ejecuta la prueba en el orden en que se especifican (definido dentro del archivo de clase compilado). También presenta una suite AllTests para ejecutar pruebas definidas por subpaquete primero. Al usar la implementación de AllTests, se puede extender la solución al filtrar también las propiedades (solíamos usar anotaciones @Fast pero aún no se publicaron).

Martin Kersten
fuente
0

Aquí hay una extensión de JUnit que puede producir el comportamiento deseado: https://github.com/aafuks/aaf-junit

Sé que esto va en contra de los autores de la filosofía JUnit, pero cuando se usa JUnit en entornos que no son pruebas unitarias estrictas (como se practica en Java), esto puede ser muy útil.

shlomi33
fuente
0

Terminé aquí pensando que mis pruebas no se ejecutaron en orden, pero la verdad es que el desastre estaba en mis trabajos asíncronos. Cuando trabaje con concurrencia, también debe realizar verificaciones de concurrencia entre sus pruebas. En mi caso, los trabajos y las pruebas comparten un semáforo, por lo que las próximas pruebas se suspenden hasta que el trabajo en ejecución libera el bloqueo.

Sé que esto no está completamente relacionado con esta pregunta, pero tal vez podría ayudar a abordar el problema correcto

McCoy
fuente
0

puede usar uno de estos códigos:

@FixMethodOrder(MethodSorters.JVM)OR `@FixMethodOrder(MethodSorters.DEFAULT)` OR `@FixMethodOrder(MethodSorters.NAME_ASCENDING)` before your test class like this:


@FixMethodOrder(MethodSorters.NAME_ASCENDING)


public class BookTest { ...}
Arezoo Bagherzadi
fuente