tutorial test suma software español ejercicios ejemplos ejemplo configuracion java unit-testing junit junit4

java - test - Cómo compartir la lógica JUnit BeforeClass entre varias clases de prueba



junit tutorial español (3)

Me encontré con un problema similar (Spring no era una opción y no escribo TestSuites en varios proyectos), así que escribí un junit runner simple para resolver este problema.

Debe escribir la clase SharedResource y marcar su prueba para requerir ese recurso.

public class SampleSharedResource implements SharedResource { public void initialize() throws Exception { //init your resource }

}

@RunWith(JUnitSharedResourceRunner.class) @JUnitSharedResourceRunner.WithSharedResources({SampleSharedResource.class}) public class SharedResourceRunnerATest { ...

Fuentes en https://github.com/eanlr/junit-shared-resources-runner

Actualmente, todas mis pruebas de JUnit se extienden desde una clase base común que proporciona métodos etiquetados con las anotaciones @BeforeClass y @AfterClass ; todo esto realmente es configurar un conjunto de recursos / servicios estáticos para que las @AfterClass .

Esto me parece un poco incómodo por varias razones:

  1. Parte del punto de JUnit4 (a mi entender) es que ya no deberíamos necesitar esta herencia de prueba clásica.
  2. Cuando ejecuto estas pruebas como parte de una suite en lugar de individualmente (lo que a menudo hacemos), se invoca a @BeforeClass y @AfterClass varias veces, ralentizando las pruebas; realmente solo deberíamos llamarlas una vez

Lo que me gustaría hacer es mover de algún modo la lógica de BeforeClass / AfterClass actual de la cadena de herencia a algo que puedan compartir las pruebas individuales y la suite en su conjunto.

Se puede hacer esto? ¿Si es así, cómo? (Si importa, estoy usando JUnit 4.7, y podría ser difícil venderlo para una versión diferente)


Puede usar @BeforeClass y @AfterClass EN LA CLASE DE SUITE .

Esto ejecutará los métodos antes de que se ejecute cualquiera de las clases de prueba en la suite y después de que todas las clases de prueba terminen (respectivamente)

De esta manera puedes correrlos solo una vez.

//..usual @RunWith etc annotations here public class MySuite{ @BeforeClass public static void setup(){ } @AfterClass public static void tearDown(){ } }


Una solución al primer problema es mover la lógica a una extensión de org.junit.rules.ExternalResource conectada a la prueba a través de una @ClassRule , introducida en JUnit 4.9:

public class MyTest { @ClassRule public static final TestResources res = new TestResources(); @Test public void testFoo() { // test logic here } } public class TestResources extends ExternalResource { protected void before() { // Setup logic that used to be in @BeforeClass } protected void after() { // Setup logic that used to be in @AfterClass } }

De esta manera, los recursos administrados previamente por la clase base se mueven fuera de la jerarquía de clases de prueba y se convierten en "recursos" más modulares / consumibles que pueden crearse antes de que una clase se ejecute y destruirse después de que se ejecute una clase.

Sin embargo, para resolver ambos problemas al mismo tiempo, es decir, tener el mismo nivel de configuración / desmontaje ejecutado como parte de una prueba individual y como parte de una suite, no parece haber ningún soporte integrado específico para esto. . Sin embargo ... , podrías implementarlo tú mismo :

Simplemente cambie la creación del recurso @ClassRule a un patrón de fábrica que haga referencia al conteo interno para determinar si crear o destruir el recurso.

Por ejemplo (tenga en cuenta que esto es difícil y podría necesitar algunos ajustes / manejo de errores para mayor robustez):

public class TestResources extends ExternalResource { private static int refCount = 0; private static TestResources currentInstance; public static TestResources getTestResources () { if (refCount == 0) { // currentInstance either hasn''t been created yet, or after was called on it - create a new one currentInstance = new TestResources(); } return currentInstance; } private TestResources() { System.out.println("TestResources construction"); // setup any instance vars } protected void before() { System.out.println("TestResources before"); try { if (refCount == 0) { System.out.println("Do actual TestResources init"); } } finally { refCount++; } } protected void after() { System.out.println("TestResources after"); refCount--; if (refCount == 0) { System.out.println("Do actual TestResources destroy"); } } }

@ClassResource clases de suite / prueba solo usarían el recurso como @ClassResource través del método de fábrica:

@RunWith(Suite.class) @SuiteClasses({FooTest.class, BarTest.class}) public class MySuite { @ClassRule public static TestResources res = TestResources.getTestResources(); @BeforeClass public static void suiteSetup() { System.out.println("Suite setup"); } @AfterClass public static void suiteTeardown() { System.out.println("Suite teardown"); } } public class FooTest { @ClassRule public static TestResources res = TestResources.getTestResources(); @Test public void testFoo() { System.out.println("testFoo"); } } public class BarTest { @ClassRule public static TestResources res = TestResources.getTestResources(); @Test public void testBar() { System.out.println("testBar"); } }

Cuando se ejecuta una prueba individual, el refcounting no tendrá ningún efecto; el "inicio real" y el "desmontaje real" solo ocurrirán una vez. Cuando se ejecuta a través de la suite, la suite creará el TestResource, y las pruebas individuales solo reutilizarán la ya instanciada (el refcounting evita que se destruya realmente y se vuelva a crear entre las pruebas de la suite).