Haczyk przed i po wykonaniu pakietu w jUnit 4.x

84

Próbuję przeprowadzić konfigurację i porzucić zestaw testów integracyjnych, używając jUnit 4.4 do wykonania testów. Rozerwanie musi przebiegać niezawodnie. Mam inne problemy z TestNG, więc szukam przeniesienia z powrotem do jUnit. Jakie punkty zaczepienia są dostępne do wykonania przed uruchomieniem jakichkolwiek testów i po zakończeniu wszystkich testów?

Uwaga: używamy maven 2 do naszej kompilacji. Próbowałem używać faz pre-& maven post-integration-test, ale jeśli test się nie powiedzie, maven zatrzymuje się i nie działa post-integration-test, co nie pomaga.

sblundy
źródło
1
Do testów integracyjnych należy używać wtyczki maven-failafe-plugin zamiast surefire. Nie zostanie to pominięte, post-integration-testjeśli test się nie powiedzie. Zobacz także tę stronę wiki .
Chris H.
czy możesz podzielić się końcową realizacją?
vikramvi,

Odpowiedzi:

113

Tak, możliwe jest niezawodne uruchamianie metod konfiguracji i usuwania metod przed i po testach w zestawie testów. Pokażę w kodzie:

package com.test;

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

@RunWith(Suite.class)
@SuiteClasses({Test1.class, Test2.class})
public class TestSuite {

    @BeforeClass
    public static void setUp() {
        System.out.println("setting up");
    }

    @AfterClass
    public static void tearDown() {
        System.out.println("tearing down");
    }

}

Twoja Test1klasa wyglądałaby mniej więcej tak:

package com.test;

import org.junit.Test;


public class Test1 {
    @Test
    public void test1() {
        System.out.println("test1");
    }

}

... i możesz sobie wyobrazić, że Test2wygląda podobnie. Gdybyś biegał TestSuite, dostałbyś:

setting up
test1
test2
tearing down

Możesz więc zobaczyć, że konfiguracja / zrywanie działa tylko odpowiednio przed i po wszystkich testach.

Haczyk: działa to tylko wtedy, gdy uruchamiasz zestaw testów i nie uruchamiasz Test1 i Test2 jako oddzielnych testów JUnit. Wspomniałeś, że używasz mavena, a wtyczka Maven surefire lubi uruchamiać testy indywidualnie, a nie jako część pakietu. W tym przypadku zalecałbym utworzenie nadklasy, którą rozszerza każda klasa testowa. Nadklasa zawiera następnie metody @BeforeClass i @AfterClass z adnotacjami. Chociaż nie jest tak czysta jak powyższa metoda, myślę, że będzie działać dla Ciebie.

Jeśli chodzi o problem z nieudanymi testami, możesz ustawić maven.test.error.ignore tak, aby kompilacja była kontynuowana po nieudanych testach. Nie jest to zalecane jako ciągła praktyka, ale powinno zapewnić ci funkcjonowanie, dopóki wszystkie testy nie przejdą. Więcej szczegółów można znaleźć w dokumentacji Maven surefire .

Julie
źródło
2
To zadziałało idealnie dla mnie, gdy wszedłem do wtyczki maven-surefire-plugin i utworzyłem listę zawiera, która wskazywała na pakiet, który chciałem uruchomić.
Jherico
2
Począwszy od JUnit 4.8.2, nie działa to dobrze z parametrami testów. Metody @BeforeClass pakietu zostaną uruchomione po metodzie @ Parameterized.Parameters testu, zapobiegając jakiejkolwiek zależności od konfiguracji pakietu.
Anm,
W odpowiedzi na mnie, gdy używam @Theories, wywołanie metody @DataPoints jest wywoływane po @BeforeClass pakietu.
Anm
18
Przepraszam za nekro - ale dodanie BeforeClass / AfterClass do superklasy nie działa zgodnie z oczekiwaniami - są one nadal wywoływane po zakończeniu każdej klasy testowej. To jest dla potomnych.
Subu Sankara Subramanian
3
Czy to nadal aktualne podejście? Jak uniknąć konieczności wyliczania listy klas testowych w adnotacji SuiteClasses?
Burhan Ali
33

Mój kolega zasugerował, co następuje: możesz użyć niestandardowego RunListener i zaimplementować metodę testRunFinished (): http://junit.sourceforge.net/javadoc/org/junit/runner/notification/RunListener.html#testRunFinished(org. junit.runner.Result)

Aby zarejestrować RunListener, po prostu skonfiguruj pewną wtyczkę w następujący sposób: http://maven.apache.org/surefire/maven-surefire-plugin/examples/junit.html sekcja „Korzystanie z niestandardowych nasłuchiwaczy i reporterów”

Ta konfiguracja powinna również zostać wybrana przez bezpieczną wtyczkę. To rozwiązanie jest świetne, ponieważ nie musisz określać pakietów, wyszukiwać klas testowych ani nic z tego - pozwala Mavenowi wykonać swoją magię, czekając na zakończenie wszystkich testów.

Martin Vysny
źródło
5
+1 To pierwsze użyteczne rozwiązanie, jakie widziałem bez uciążliwej obsługi klasy Suite!
Stefan Haberl
7

Używając adnotacji, możesz zrobić coś takiego:

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

class SomethingUnitTest {
    @BeforeClass
    public static void runBeforeClass()
    {

    }

    @AfterClass
    public static void runAfterClass()
    {  

    }

    @Before  
    public void setUp()
    {

    }

    @After
    public void tearDown()
    {

    }

    @Test
    public void testSomethingOrOther()
    {

    }

}
Jroc
źródło
4
Konfigurację i dezaktywację należy wykonać raz na przebieg. Pomogłoby to tylko wtedy, gdyby wszystkie testy odbywały się w jednej klasie.
sblundy
3

Tutaj my

  • zaktualizowany do JUnit 4.5,
  • napisali adnotacje, aby oznaczyć każdą klasę testową lub metodę, która wymagała działającej usługi,
  • napisałem handlery dla każdej adnotacji, które zawierały statyczne metody implementacji konfiguracji i dezaktywacji usługi,
  • rozszerzyło zwykłego Runnera o lokalizowanie adnotacji w testach, dodając statyczne metody obsługi do łańcucha wykonywania testów w odpowiednich punktach.

źródło
2

Jeśli chodzi o „Uwaga: używamy maven 2 do naszej kompilacji. Próbowałem użyć faz testów maven przed i po integracji, ale jeśli test się nie powiedzie, maven zatrzymuje się i nie uruchamia testu po integracji co nie jest pomocne ”.

zamiast tego możesz wypróbować wtyczkę failafe-plugin, myślę, że ma ona funkcję zapewniającą czyszczenie niezależnie od konfiguracji lub statusu etapu pośredniego

Binod Pant
źródło
Tak, bezpieczna wtyczka pozwoli ci określić konkretną konfigurację i dezaktywację. Chociaż nie sądzę, aby istniało bezpieczeństwo w momencie wysłania tego pytania.
Jason Axelson
2

Zakładając, że wszystkie Twoje testy mogą rozszerzać klasę „techniczną” i znajdują się w tym samym pakiecie, możesz zrobić małą sztuczkę:

public class AbstractTest {
  private static int nbTests = listClassesIn(<package>).size();
  private static int curTest = 0;

  @BeforeClass
  public static void incCurTest() { curTest++; }

  @AfterClass
  public static void closeTestSuite() {
      if (curTest == nbTests) { /*cleaning*/ }             
  }
}

public class Test1 extends AbstractTest {
   @Test
   public void check() {}
}
public class Test2 extends AbstractTest {
   @Test
   public void check() {}
}

Pamiętaj, że to rozwiązanie ma wiele wad:

  • musi wykonać wszystkie testy pakietu
  • musi podklasować klasę „techniczną”
  • nie możesz używać @BeforeClass i @AfterClass wewnątrz podklas
  • jeśli wykonasz tylko jeden test w pakiecie, czyszczenie nie zostanie wykonane
  • ...

Więcej informacji: listClassesIn () => Jak znaleźć wszystkie podklasy danej klasy w Javie?

christophe blin
źródło
1
nie jest to prawdą, o ile pokazują moje własne testy. Mam super klasę, która uruchamia osadzoną rybkę szklistą na przedklasie i wyłącza ją po zajęciach. Mam wtedy 2 klasy, które wykraczają poza tę superklasę. Klasa beforeclass jest wykonywana przed uruchomieniem testów zdefiniowanych w każdej klasie.
Jonathan Morales Vélez
0

O ile wiem, nie ma takiego mechanizmu w JUnit, jednak można spróbować podklasy Suite i nadpisać metodę run () wersją, która zapewnia podpięcia.

user15299
źródło
0

Ponieważ maven-surefire-plugin nie uruchamia najpierw klasy Suite, ale traktuje klasy Suite i testowe tak samo, więc możemy skonfigurować wtyczkę jak poniżej, aby włączyć tylko klasy z pakietu i wyłączyć wszystkie testy. Suite uruchomi wszystkie testy.

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.5</version>
            <configuration>
                <includes>
                    <include>**/*Suite.java</include>
                </includes>
                <excludes>
                    <exclude>**/*Test.java</exclude>
                    <exclude>**/*Tests.java</exclude>
                </excludes>
            </configuration>
        </plugin>
Anurag Sharma
źródło
0

Myślę, że jedynym sposobem uzyskania pożądanej funkcjonalności byłoby zrobienie czegoś takiego

import junit.framework.Test;  
import junit.framework.TestResult;  
import junit.framework.TestSuite;  

public class AllTests {  
    public static Test suite() {  
        TestSuite suite = new TestSuite("TestEverything");  
        //$JUnit-BEGIN$  
        suite.addTestSuite(TestOne.class);  
        suite.addTestSuite(TestTwo.class);  
        suite.addTestSuite(TestThree.class);  
        //$JUnit-END$  
     }  

     public static void main(String[] args)  
     {  
        AllTests test = new AllTests();  
        Test testCase = test.suite();  
        TestResult result = new TestResult();  
        setUp();  
        testCase.run(result);  
        tearDown();  
     }  
     public void setUp() {}  
     public void tearDown() {}  
} 

Używam czegoś takiego w Eclipse, więc nie jestem pewien, jak przenośny jest poza tym środowiskiem

Jroc
źródło
3
To jest przykład dla JUnit3, a OP zapytał o JUnit4, ale na wypadek, gdyby niektórzy użytkownicy JUnit3 znaleźli to pytanie ... W przypadku JUnit3 lepiej byłoby pozbyć się metody main () i mieć metodę suite () opakuj TestSuite w podklasę junit.extensions.TestSetup. Nadal masz te same zastrzeżenia, co przykład Julie, dotyczący uruchamiania poszczególnych klas testowych w swoim IDE.
NamshubWriter
0

Jeśli nie chcesz tworzyć zestawu i musisz wymieniać wszystkie swoje klasy testowe, możesz użyć refleksji, aby znaleźć liczbę klas testowych dynamicznie i odliczać w dół w klasie bazowej @AfterClass, aby wykonać tearDown tylko raz:

public class BaseTestClass
{
    private static int testClassToRun = 0;

    // Counting the classes to run so that we can do the tear down only once
    static {
        try {
            Field field = ClassLoader.class.getDeclaredField("classes");
            field.setAccessible(true);

            @SuppressWarnings({ "unchecked", "rawtypes" })
            Vector<Class> classes = (Vector<Class>) field.get(BlockJUnit4ClassRunner.class.getClassLoader());
            for (Class<?> clazz : classes) {
                if (clazz.getName().endsWith("Test")) {
                    testClassToRun++;
                }
            }
        } catch (Exception ignore) {
        }
    }

    // Setup that needs to be done only once
    static {
        // one time set up
    }

    @AfterClass
    public static void baseTearDown() throws Exception
    {
        if (--testClassToRun == 0) {
            // one time clean up
        }
    }
}

Jeśli wolisz używać @BeforeClass zamiast bloków statycznych, możesz również użyć flagi boolowskiej, aby wykonać liczenie odbić i przetestować konfigurację tylko raz przy pierwszym wywołaniu. Mam nadzieję, że to komuś pomoże, zajęło mi całe popołudnie, aby wymyślić lepszy sposób niż wyliczenie wszystkich klas w zestawie.

Teraz wszystko, co musisz zrobić, to rozszerzyć tę klasę na wszystkie klasy testowe. Mieliśmy już klasę bazową, która zapewniała kilka wspólnych elementów dla wszystkich naszych testów, więc było to dla nas najlepsze rozwiązanie.

Inspiracja pochodzi z tej odpowiedzi SO https://stackoverflow.com/a/37488620/5930242

Jeśli nie chcesz rozszerzać tej klasy wszędzie, ta ostatnia odpowiedź SO może zrobić, co chcesz.

FredBoutin
źródło