Junit: ejecutar el método de configuración una vez

119

Configuré una clase con un par de pruebas y, en lugar de usar, @Beforeme gustaría tener un método de configuración que se ejecute solo una vez antes de todas las pruebas. ¿Es eso posible con Junit 4.8?

Bober02
fuente
1
Eche un vistazo a RunListener: stackoverflow.com/a/14773170/548473
Grigory Kislin

Respuestas:

205

Aunque estoy de acuerdo con @assylias en que usar @BeforeClasses una solución clásica, no siempre es conveniente. El método anotado con @BeforeClassdebe ser estático. Es muy inconveniente para algunas pruebas que necesitan una instancia de caso de prueba. Por ejemplo, pruebas basadas en Spring que usan@Autowired funcionar con servicios definidos en el contexto de Spring.

En este caso, personalmente uso el setUp()método regular anotado con @Beforeanotación y administro mi bandera personalizada static(!) boolean:

private static boolean setUpIsDone = false;
.....
@Before
public void setUp() {
    if (setUpIsDone) {
        return;
    }
    // do the setup
    setUpIsDone = true;
}
AlexR
fuente
10
Añadiendo al comentario de Kenny Cason sobre por qué debe ser estático. Debe ser estático porque JUnit crea una nueva instancia de la clase de prueba para cada método @Test. La variable de instancia se restablecerá a su valor predeterminado (falso) para cada instancia si no es estática. Consulte para obtener más información: martinfowler.com/bliki/JunitNewInstance.html
dustin.schultz
2
Esto funciona, excepto en el caso de que el setUp()método esté en una superclase: he publicado una respuesta a continuación para intentar resolver esto.
Steve Chambers
4
Dudo en decirle esto a alguien con un representante de 84k, pero BeforeClass de hecho no responde a la pregunta: BeforeClass se ejecuta al comienzo de cada clase de prueba. Pero el OP pidió uno que se ejecute "solo una vez antes de todas las pruebas". Su solución propuesta podría hacer esto, pero tendría que hacer que todas sus clases de prueba extendieran una clase "CommonTest" ...
Mike
1
@mikerodent, en mi humilde opinión OP preguntó sobre todas las pruebas dentro de su caso de prueba, no todas las pruebas en general. Entonces, tu comentario es menos relevante. Por cierto, no se preocupe por decirle nada a nadie, incluso si su reputación es alta. Al menos esto es lo que hago :). Y mi reputación era significativamente más baja en agosto de 2012 cuando respondí la pregunta.
AlexR
No funciona para mi caso, las variables inicializadas en la configuración se reinician después de cada prueba, por lo que no tiene sentido iniciar solo una vez.
Aphax
89

Puede utilizar la BeforeClassanotación :

@BeforeClass
public static void setUpClass() {
    //executed only once, before the first test
}
Assylias
fuente
12
No puedo usar esto, tengo algunos métodos de configuración que se basan en componentes no estáticos como getClass ()
Bober02
1
@ Bober02 BeforeClass debe ser estático. Si no puede usar eso, la otra respuesta proporciona una solución.
assylias
2
¿Seguro que no puede usar en TheClassYouWant.classlugar de su llamada getClass ()? Esto es real de Java: String.class.getName().
stolsvik
1
@mikerodent Entendí la pregunta como "todas las pruebas en la clase", pero tienes razón, puede que no sea lo que quería el OP.
Assylias
29

JUnit 5 ahora tiene una anotación @BeforeAll:

Indica que el método anotado debe ejecutarse antes que todos los métodos @Test en la clase actual o jerarquía de clases; análogo a @BeforeClass de JUnit 4. Tales métodos deben ser estáticos.

¡Las anotaciones del ciclo de vida de JUnit 5 parecen haberlo hecho bien finalmente! Puede adivinar qué anotaciones están disponibles sin siquiera mirar (por ejemplo, @BeforeEach @AfterAll)

Brian
fuente
6
Tiene el mismo problema @BeforeClass, tiene que serlo static. La solución de IMO @ AlexR es mejor.
zengr
@zengr tiende a estar de acuerdo contigo: como le he dicho a AlexR, su solución requiere que todas las clases de prueba se subclasifiquen de una clase CommonTest si solo se ejecuta una vez. Pero es tan simple como puede ser, y en mi humilde opinión, probablemente no debería usar una solución "elegante" suministrada por el marco cuando un mecanismo simple está disponible en el lenguaje. A menos que haya una buena razón, por supuesto. Además, usar una cosa simple como la suya, con un buen nombre de tipo "hace lo que dice en la lata", ayuda con la legibilidad.
Mike Rodent
Habiendo dicho esto, nuevamente en mi humilde opinión, parece que hay mucha más justificación para tener una anotación "AfterAll": sería muy difícil e ingenioso idear un mecanismo para detectar cuándo se realizaron todas las pruebas. Por el contrario, por supuesto, los puristas probablemente dirán que nunca debería tener que hacer una "limpieza final", es decir, que cada "tearDown" debería dejar todos los recursos en un estado prístino ... ¡y probablemente tengan razón!
Mike Rodent
¿Funciona esto con Maven donde hay varios módulos, cada uno con sus pruebas?
Mark Boon
@mike rodent, en mi caso, la configuración y eliminación de archivos de prueba en el sistema de archivos antes / después de cada prueba parece llevar a puntos muertos en los archivos. Por ahora, he llegado de forma independiente a la solución de AlexR para configurar una vez. Tengo dos banderas estáticas, ya están configuradas y sucias. setup () llama a cleanup () si inicialmente se detecta un estado sucio o si una falla de configuración conduce a un estado sucio. Para limpiar después de ejecutar las pruebas, las ejecuto nuevamente. Desordenado, para nada ideal, no en nuestro proceso de construcción. Sigo buscando una forma mejor (jUnit 4.12).
Rebeccah
9

Cuando setUp()está en una superclase de la clase de prueba (por ejemplo, a AbstractTestBasecontinuación), la respuesta aceptada se puede modificar de la siguiente manera:

public abstract class AbstractTestBase {
    private static Class<? extends AbstractTestBase> testClass;
    .....
    public void setUp() {
        if (this.getClass().equals(testClass)) {
            return;
        }

        // do the setup - once per concrete test class
        .....
        testClass = this.getClass();
    }
}

Esto debería funcionar para un solo setUp()método no estático , pero no puedo producir un equivalente tearDown()sin perderme en un mundo de reflexión compleja ... ¡Puntos de recompensa para cualquiera que pueda!

Steve Cámaras
fuente
3

Editar: descubrí mientras depuraba que la clase también se instancia antes de cada prueba. Supongo que la anotación @BeforeClass es la mejor aquí.

También puede configurar el constructor, la clase de prueba es una clase después de todo. No estoy seguro de si es una mala práctica porque casi todos los demás métodos están anotados, pero funciona. Podrías crear un constructor como ese:

public UT () {
    // initialize once here
}
@Test
// Some test here...

El ctor se llamará antes de las pruebas porque no son estáticas.


fuente
0

Pruebe esta solución: https://stackoverflow.com/a/46274919/907576 :

con @BeforeAllMethods/ @AfterAllMethodsannotation puede ejecutar cualquier método en la clase Test en un contexto de instancia, donde todos los valores inyectados están disponibles.

radistao
fuente
Se basa en una biblioteca de terceros.
Andrew
0

Mi solución sucia es:

public class TestCaseExtended extends TestCase {

    private boolean isInitialized = false;
    private int serId;

    @Override
    public void setUp() throws Exception {
        super.setUp();
        if(!isInitialized) {
            loadSaveNewSerId();
            emptyTestResultsDirectory();
            isInitialized = true;
        }
    }

   ...

}

Lo uso como base para todos mis testCases.

Obi Two
fuente
La clase pública TestCaseExtended extiende TestCase {privado estático booleano isInitialized = false; Privado estático TestCaseExtended caseExtended; private int serId; @Override public void setUp () lanza Exception {super.setUp (); if (! isInitialized) {caseExtended = new TestCaseExtended (); caseExtended.loadSaveNewSerId (); caseExtended.emptyTestResultsDirectory (); isInitialized = true; }}
Obi Two
0

Si no desea forzar la declaración de una variable que se establece y se verifica en cada subprueba, agregar esto a una SuperTest podría hacer:

public abstract class SuperTest {

    private static final ConcurrentHashMap<Class, Boolean> INITIALIZED = new ConcurrentHashMap<>();
    protected final boolean initialized() {
        final boolean[] absent = {false};
        INITIALIZED.computeIfAbsent(this.getClass(), (klass)-> {
            return absent[0] = true;
        });
        return !absent[0];
    }
}



public class SubTest extends SuperTest {
    @Before
    public void before() {
        if ( super.initialized() ) return;

         ... magic ... 
    }

}
mmm
fuente
0

Resolví este problema así:

Agregue a su clase abstracta base (me refiero a la clase abstracta donde inicializa su controlador en el método setUpDriver () ) esta parte del código:

private static boolean started = false;
static{
    if (!started) {
        started = true;
        try {
            setUpDriver();  //method where you initialize your driver
        } catch (MalformedURLException e) {
        }
    }
}

Y ahora, si sus clases de prueba se extienden desde la clase abstracta Base -> el método setUpDriver () se ejecutará antes del primer @Test solo UNA vez por ejecución.

Sergii
fuente
0

Use el método @PostConstruct de Spring para hacer todo el trabajo de inicialización y este método se ejecuta antes de que se ejecute cualquiera de las @Test

Abhishek Chatterjee
fuente