Test JUnit z dynamiczną liczbą testów

95

W naszym projekcie mam kilka testów JUnit , które np. Pobierają każdy plik z katalogu i uruchamiają na nim test. Jeśli zaimplementuję testEveryFileInDirectorymetodę w, TestCaseto pokazuje się jako tylko jeden test, który może się nie powieść lub zakończyć. Ale interesują mnie wyniki dla każdego pojedynczego pliku. Jak mogę napisać TestCase/ TestSuitetakie, że każdy plik pojawia się jako osobny test, np. W graficznym TestRunnerze Eclipse? (Nie ma możliwości kodowania jawnej metody testowania dla każdego pliku).

Porównaj również pytanie ParameterizedTest z nazwą w Eclipse Testrunner .

Hans-Peter Störr
źródło

Odpowiedzi:

102

Spójrz na sparametryzowane testy w JUnit 4.

Właściwie zrobiłem to kilka dni temu. Spróbuję wyjaśnić ...

Najpierw zbuduj klasę testową normalnie, tak jak testowałeś tylko z jednym plikiem wejściowym. Udekoruj swoją klasę:

@RunWith(Parameterized.class)

Zbuduj jeden konstruktor, który pobierze dane wejściowe, które będą się zmieniać przy każdym wywołaniu testu (w tym przypadku może to być sam plik)

Następnie utwórz statyczną metodę, która zwróci a Collectionz tablic. Każda tablica w kolekcji będzie zawierała argumenty wejściowe dla twojego konstruktora klasy, np. Plik. Udekoruj tę metodę:

@Parameters

Oto przykładowa klasa.

@RunWith(Parameterized.class)
public class ParameterizedTest {

    private File file;

    public ParameterizedTest(File file) {
        this.file = file;
    }

    @Test
    public void test1() throws Exception {  }

    @Test
    public void test2() throws Exception {  }

    @Parameters
    public static Collection<Object[]> data() {
        // load the files as you want
        Object[] fileArg1 = new Object[] { new File("path1") };
        Object[] fileArg2 = new Object[] { new File("path2") };

        Collection<Object[]> data = new ArrayList<Object[]>();
        data.add(fileArg1);
        data.add(fileArg2);
        return data;
    }
}

Sprawdź również ten przykład

bruno conde
źródło
1
Dzięki! Metoda JUnit 4 jest lepsza niż metoda JUnit 3 podana w innej odpowiedzi, ponieważ JUnit 3 myli biegacz testów zaćmienia, a metoda JUnit 4 umożliwia ponowne wykonanie testów itp. Zastanawiam się tylko, jak mogę wyświetlić zaćmienie nazwa testu - pokazuje tylko [0], [1] itd.
Hans-Peter Störr
@hstoerr, Wygląda na to, że będzie to w następnym wydaniu JUnit :-) github.com/KentBeck/junit/commit/…
rescdsk
Jak byś to przekształcił, gdybyś chciał, aby każdy przebieg [z inną kombinacją danych] modyfikował nazwę tych przebiegów testowych? [To znaczy plik Path1 zostanie przetestowany jako: test1Path1, test2Path?
mnich
^ Zaktualizowany link: github.com/junit-team/junit4/commit/…
Alexander Udalov
27

JUnit 3

public class XTest extends TestCase {

    public File file;

    public XTest(File file) {
        super(file.toString());
        this.file = file;
    }

    public void testX() {
        fail("Failed: " + file);
    }

}

public class XTestSuite extends TestSuite {

    public static Test suite() {
        TestSuite suite = new TestSuite("XTestSuite");
        File[] files = new File(".").listFiles();
        for (File file : files) {
            suite.addTest(new XTest(file));
        }
        return suite;
    }

}

JUnit 4

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

@RunWith(Parameterized.class)
public class TestY {

    @Parameters
    public static Collection<Object[]> getFiles() {
        Collection<Object[]> params = new ArrayList<Object[]>();
        for (File f : new File(".").listFiles()) {
            Object[] arr = new Object[] { f };
            params.add(arr);
        }
        return params;
    }

    private File file;

    public TestY(File file) {
        this.file = file;
    }

    @Test
    public void testY() {
        fail(file.toString());
    }

}
McDowell
źródło
11

Junit 5 Sparametryzowane testy

Sparametryzowane testy JUnit 5 wspierają to, umożliwiając użycie metody jako źródła danych :

@ParameterizedTest
@MethodSource("fileProvider")
void testFile(File f) {
    // Your test comes here
}

static Stream<File> fileProvider() {
    return Arrays.asList(new File(".").list()).stream();
}

JUnit 5 DynamicTests

JUnit 5 wspiera to również poprzez pojęcie a DynamicTest, które ma być generowane w a @TestFactoryza pomocą metody statycznej dynamicTest.

import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import static org.junit.jupiter.api.DynamicTest.dynamicTest;

import java.util.stream.Stream;

@TestFactory
public Stream<DynamicTest> testFiles() {
    return Arrays.asList(new File(".").list())
            .stream()
            .map((file) -> dynamicTest(
                    "Test for file: " + file,
                    () -> { /* Your test comes here */ }));
}

Testy uruchomione w twoim IDE (tutaj IntelliJ) będą wyświetlane w następujący sposób:

Wyjście w IntelliJ

avandeursen
źródło
3

Powinno to być możliwe w JUnit 3 przez dziedziczenie TestSuitei przesłonięcie tests()metody, aby wyświetlić listę plików i dla każdego zwrócenia instancji podklasy, TestCasektóra przyjmuje nazwę pliku jako parametr konstruktora i ma metodę testową, która testuje plik podany w konstruktorze.

W JUnit 4 może być jeszcze łatwiej.

Michael Borgwardt
źródło
2

Możesz rozważyć użycie biblioteki JUnitParams , więc miałbyś kilka innych (czystszych) opcji:

@org.junit.runner.RunWith(junitparams.JUnitParamsRunner.class)
public class ParameterizedTest {

    @org.junit.Test
    @junitparams.Parameters(method = "data")
    public void test1(File file) throws Exception {  }

    @org.junit.Test
    @junitparams.Parameters(method = "data")
    public void test2(File file) throws Exception {  }

    public static File[] data() {
        return new File[] { new File("path1"), new File("path2") };
    }
}

@org.junit.runner.RunWith(junitparams.JUnitParamsRunner.class)
public class ParameterizedTest {

    @org.junit.Test
    @junitparams.Parameters(value = { "path1", "path2" })
    public void test1(String path) throws Exception {
        File file = new File(path);
    }

    @org.junit.Test
    @junitparams.Parameters(value = { "path1", "path2" })
    public void test2(String path) throws Exception {
        File file = new File(path);
    }
}

Możesz zobaczyć więcej przykładów użycia tutaj .

Oprócz JUnitParams, dlaczego pisanie za jego pomocą testów parametryzowanych jest łatwiejsze i bardziej czytelne :

Projekt JUnitParams dodaje nowy runner do JUnit i zapewnia znacznie łatwiejsze i bardziej czytelne sparametryzowane testy dla JUnit> = 4.6.

Główne różnice w stosunku do standardowej prowadnicy parametrycznej JUnit:

  • bardziej wyraźne - parametry znajdują się w parametrach metody testowej, a nie w polach klas
  • mniej kodu - nie potrzebujesz konstruktora do ustawiania parametrów
  • można mieszać sparametryzowane z niesparametryzowanymi metodami w jednej klasie
  • parametry mogą być przekazywane jako ciąg CSV lub z klasy dostawcy parametrów
  • Parametr dostawcy może mieć dowolną liczbę parametrów udostępniających metody, dzięki czemu można grupować różne przypadki
  • możesz mieć metodę testową, która zapewnia parametry (nie ma już zewnętrznych klas ani statyki)
  • możesz zobaczyć rzeczywiste wartości parametrów w swoim IDE (w parametryzowanym JUnit to tylko kolejne numery parametrów)
falsarella
źródło
1

Jeśli TestNG jest opcją, możesz użyć parametrów z dostawcami danych .

Wynik testu każdego pliku zostanie wyświetlony w raporcie tekstowym lub w interfejsie wtyczki TestNG Eclipse. Liczba wszystkich uruchomionych testów będzie liczyć każdy z Twoich plików indywidualnie.

To zachowanie różni się od teorii JUnit , w których wszystkie wyniki są skupione w jednym wpisie „teorii” i liczone jako 1 test. Jeśli chcesz osobne raportowanie wyników w JUnit, możesz wypróbować testy sparametryzowane .

Test i dane wejściowe

public class FileTest {

    @DataProvider(name="files")
    public File[][] getFiles(){
        return new File[][] {
            { new File("file1") },
            { new File("file2") }
        };
        // or scan a directory
    }

    @Test(dataProvider="files")
    public void testFile(File file){
        //run tests on file
    }
}

Przykładowe dane wyjściowe

PASSED: testFile(file1)
PASSED: testFile(file2)

===============================================
    Default test
    Tests run: 2, Failures: 0, Skips: 0
===============================================
Ben Hutchison
źródło
Nie znam teorii, ale sparametryzowane testy w JUnit są pokazywane osobno w zaćmieniu, a nie skupione razem.
Hans-Peter Störr