Тест JUnit с динамическим количеством тестов
В нашем проекте у меня есть несколько тестов JUnit, которые, например, берут каждый файл из каталога и запускают тест для него. Если я реализую testEveryFileInDirectory
метод в TestCase
это проявляется только в качестве одного теста, который может быть неудачным или успешным. Но меня интересуют результаты по каждому отдельному файлу. Как я могу написать TestCase
/ TestSuite
такой, что каждый файл отображается как отдельный тест, например, в графическом TestRunner Eclipse? (Кодирование явного метода тестирования для каждого файла не вариант.)
Сравните также вопрос ParameterizedTest с именем в Eclipse Testrunner.
7 ответов
Взгляните на параметризованные тесты в JUnit 4.
На самом деле я сделал это несколько дней назад. Я постараюсь объяснить...
Сначала соберите свой тестовый класс как обычно, так как вы тестируете только один входной файл. Украсьте свой класс:
@RunWith(Parameterized.class)
Создайте один конструктор, который принимает входные данные, которые будут меняться при каждом вызове теста (в данном случае это может быть сам файл)
Затем создайте статический метод, который будет возвращать Collection
массивов. Каждый массив в коллекции будет содержать входные аргументы для вашего конструктора класса, например, файла. Украсьте этот метод:
@Parameters
Вот пример класса.
@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;
}
}
Также проверьте этот пример
Юнит 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;
}
}
Юнит 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());
}
}
Junit 5 параметризованных тестов
Параметризованные тесты JUnit 5 поддерживают это, позволяя использовать метод в качестве источника данных:
@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 также поддерживает это через понятие DynamicTest
, который должен быть создан в @TestFactory
с помощью статического метода 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 */ }));
}
Тесты, запущенные в вашей IDE (IntelliJ здесь), будут отображаться так:
Должно быть возможно в JUnit 3 путем наследования от TestSuite
и переопределяя tests()
метод для перечисления файлов и для каждого возврата экземпляр подкласса TestCase
он принимает имя файла в качестве параметра конструктора и имеет метод test, который проверяет файл, заданный в конструкторе.
В JUnit 4 это может быть даже проще.
Вы можете рассмотреть возможность использования библиотеки JUnitParams, чтобы у вас было несколько более (более чистых) вариантов:
@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);
}
}
Вы можете увидеть больше примеров использования здесь.
В дополнение к JUnitParams, почему написание параметризованных тестов с ним проще и удобочитаемее:
Проект JUnitParams добавляет новый бегунок в JUnit и предоставляет гораздо более простые и удобочитаемые параметризованные тесты для JUnit >=4.6.
Основные отличия от стандартного бегунка JUnit Parametrised:
- более явно - параметры находятся в параметрах метода тестирования, а не в полях класса
- меньше кода - вам не нужен конструктор для настройки параметров
- Вы можете смешивать параметризованные с непараметрическими методами в одном классе
- параметры могут быть переданы в виде строки CSV или из класса поставщика параметров
- у класса провайдера параметров может быть столько параметров, сколько вам нужно, чтобы вы могли группировать разные случаи
- у вас может быть метод теста, который предоставляет параметры (больше нет внешних классов или статики)
- вы можете увидеть фактические значения параметров в вашей IDE (в Parametrised JUnit это только последовательные числа параметров)
Если TestNG является опцией, вы можете использовать параметры с DataProviders.
Результаты каждого отдельного файла будут показаны в текстовом отчете или в пользовательском интерфейсе плагина Eclipse TestNG. Общее количество выполненных тестов будет подсчитывать каждый из ваших файлов в отдельности.
Это поведение отличается от JUnit Theories, в котором все результаты объединены в одну "теорию" и считаются только 1 тестом. Если вам нужна отдельная отчетность о результатах в JUnit, вы можете попробовать параметризованные тесты.
Тест и входы
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
}
}
Пример вывода
PASSED: testFile(file1)
PASSED: testFile(file2)
===============================================
Default test
Tests run: 2, Failures: 0, Skips: 0
===============================================
У меня была похожая проблема, и в итоге я написал простой JUnit 4 Runner, который позволяет меду динамически генерировать тесты.