Estoy usando JUnit 4 para probar un sistema backend con una base de datos en memoria. Estoy usando @BeforeClass @Before @After y @AfterClass .

Está funcionando muy bien hasta ahora a nivel de clase.

@BeforeClass contiene la configuración de la base de datos que es lenta pero solo debe realizarse una vez por sesión de prueba.

@Before simplemente limpia la pizarra para que se ejecute la siguiente prueba. Es bastante rapido.

Mis pruebas se parecen a esto:

class CompanyTest  {

    @BeforeClass
    public static void beforeAll() throws Exception {
        BeforeAll.run(); //Setup In Memory Database!!! Very time intensive!!!
    }
    @Before
    public void beforeTest() throws Exception {
        BeforeTest.run(); //Setup data in database. Relatively quick
    }


    @Test
    public void testCreateCompany() throws Exception {
        ///...
    }
    @Test
    public void testDeleteCompany() throws Exception {
        ///...
    }
    @Test
    public void testAdminCompany() throws Exception {
        ///...
    }


    @After
    public void afterTest() {
        AfterTest.run(); //clear data
    }
    @AfterClass
    public static void afterAll() throws Exception {
        AfterAll.run(); //tear down database
    }
}

Está funcionando muy bien hasta ahora a nivel de clase.

Puedo hacer clic derecho (en Eclipse) en una prueba individual y ejecutará @BeforeClass y luego @Before .

También puedo hacer clic (en Eclipse) en la clase en sí, y se ejecutará @BeforeClass solo una vez y luego @Before antes de cada prueba.

.... pero ¿cómo se extiende este principio al nivel Suite?

Quiero ejecutar @BeforeClass antes de todas mis clases en mi Suite. Si escribo mi Suite así:

@Suite.SuiteClasses({ CompanyTest.class, CustomerTest.class, SomeOtherTest.class, })

public class AllTests {

    @BeforeClass
    public static void beforeAll() throws Exception {
        BeforeAll.run();
    }

    @AfterClass
    public static void afterAll() throws Exception {
        AfterAll.run();
    }
}

... Necesito eliminar @BeforeClass de todas mis clases de prueba. Esto es molesto porque tengo muchas clases de prueba y no quiero eliminar mi @BeforeClass porque quiero probarlas de forma individual.

Lo que básicamente estoy diciendo es:

¿Existe una manera fácil (clic de un mouse en IDE) para probar las pruebas JUnit en el (a) nivel de método, (b) nivel de clase y (c) nivel de suite, mientras se mantiene un proceso de configuración y desmontaje a nivel de sesión? ?

0
Oliver Watkins 16 oct. 2018 a las 15:21

2 respuestas

La mejor respuesta

Lo que necesita es que el estado global se gestione a través de diferentes puntos de entrada. En su clase BeforeAll o AfterAll, puede mantener una referencia estática, es decir, una AtomicBoolean para rastrear si ya la ejecutó o no.

El problema ahora, con dos clases separadas para beforeAll y afterAll, qué clase debería ser responsable de esa bandera.

Por lo tanto, le sugiero que defina un Rule que contenga todos los lógica para la configuración y desmontaje (básicamente todo el código de BeforeAll y AfterAll) y que administra el estado global para rastrear la inicialización.

Básicamente algo como

class MyRule implements TestRule {
    static final AtomicBoolean INITIALIZED = new AtomicBoolean();

    @Override
    public Statement apply(final Statement base, final Description description) {

        return new Statement() {

            @Override
            public void evaluate() throws Throwable {
                boolean needsTearDown = false;
                try {
                    if (INITIALIZED.compareAndSet(false, true)) {
                        BeforeAll.run();
                        needsTearDown = true;
                    }
                    base.evaluate();
                } finally {
                    if (needsTearDown) {
                        AfterAll.run();
                        INITIALIZED.set(false);
                    }
                }
            }
        };
    }
}

Y en su Test y Suite solo agregue

class YourTestOrSuite {

  @ClassRule
  public static MyRule rule = new MyRule();
}
1
Gerald Mücke 17 oct. 2018 a las 13:34

Entonces, tengo un problema similar (inicialización de base de datos, grupo de conexiones, datos en caché, etc.) que debe hacerse una vez antes de ejecutar todas y cada una de las pruebas. Esto se hace normalmente cuando se inicia el servidor de aplicaciones. Para las pruebas que requieren esto, agregué una clase base:

public class BaseInitializedTest {
    private boolean isInitialized;

    @BeforeClass
    public static void init() {
        doOnetimeInit();

        // Do other per unique class initialization here
    }

    private static synchronized void doOnetimeInit() {
        if (!isInitialized) {
            // Do you one time init here

            isInitialized = true;
        }
    }

    @AfterClass
    public static void deinit() {
        // Cleanup
    }
}

Y luego mis pruebas que requieren inicialización:

public class SomethingTest extends BaseInitializedTest {
    @Test
    public testSomething() {
        // Your test here
    }
}

No todas las pruebas necesitan que se inicialice la pila, pero las que lo hagan heredarán de esta clase base y manejarán init correctamente. Espero que ayude.

1
AlexC 17 oct. 2018 a las 18:45