从 Java 中的循环创建多个单元测试
Creating multiple unit tests from a loop in Java
我正在为我的(小)程序编写单元测试,测试用例在大约 30 个不同的文件中指定。
为了测试它,我只需要一个遍历所有文件、解析它们并执行所需内容的循环。
问题是,在那种情况下,我的所有测试都将被视为一个测试,因为它们都在同一个函数中,使用 @Test
表示法。
是否可以以某种方式拆分它,而不必为每个测试文件提供单独的功能?
将所有测试作为一个测试用例的问题是,我看不到哪个测试用例失败了;如果一个失败,其余的失败(我会得到 1 个测试失败而不是 5/30 失败)
我目前正在使用 JUnit (4.12),但我没有义务继续使用它,所以如果有更好的解决方案,我可以切换框架。
谢谢!
示例:
public class MyTests {
@Test
public void testFromFiles {
// loop through all the files
}
}
output: 1 test run successfully
更新: 所选答案对我来说非常有用,我添加了另一个 JUnit 5 解决方案(而不是 4),以防它对某人有所帮助。
试试这个方法:
@RunWith(Parameterized.class)
public class EdiTest {
@SuppressWarnings("WeakerAccess")
@Parameterized.Parameter(value = 0)
public String model;
@SuppressWarnings("WeakerAccess")
@Parameterized.Parameter(value = 1)
public String filename;
@Parameterized.Parameters(name = "{index}: testEDI({0}, {1})")
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][]{
{"753", "edi753_A.edi"},
{"753", "edi753_B.edi"},
{"754", "edi754.edi"},
{"810", "edi810-withTax.edi"},
{"810", "edi810-withoutTax.edi"},
});
}
@Before
public void setUpContext() throws Exception {
TestContextManager testContextManager = new TestContextManager(getClass());
testContextManager.prepareTestInstance(this);
}
@Test
public void testEDI() throws IOException {
String edi = IOUtils.toString(ClassLoader.getSystemResource(filename));
EdiConverter driver = ediConverterProvider.getConverter(model);
// your test code here
}
}
使用 JUnit 5 和 Dynamic Tests:
-
class DynamicTests {
@TestFactory
List<DynamicTest> createSomeTests() {
return Arrays.asList(
DynamicTest.dynamicTest("First dynamically created test",
() -> assertTrue(true)),
DynamicTest.dynamicTest("Second dynamically created test",
() -> assertTrue(true))
);
}
}
获取和过滤所有测试文件的示例:
class MyTestsClass {
@TestFactory
List<DynamicTest> runAllTestFiles() {
List<DynamicTest> list = new ArrayList<DynamicTest>();
try (Stream<Path> paths = Files.walk(Paths.get("tests_dir"))) {
List<Path> files = paths
.filter(path -> path.getFileName().toString().endsWith(".mytests"))
.collect(Collectors.toList());
files.forEach(file -> list.add(
DynamicTest.dynamicTest(
file.getFileName().toString(),
() -> testFileWithSomeAsserts(file))
));
} catch (IOException e) {
e.printStackTrace();
}
return list;
}
}
@TestFactory
的工作方式与常规测试不同,在这种情况下无法使用 @BeforeEach
/ @AfterEach
:
Dynamic Test Lifecycle
The execution lifecycle of a dynamic test is quite different than it is for a standard @Test
case. Specifically, there are no lifecycle callbacks for individual dynamic tests. This means that @BeforeEach
and @AfterEach
methods and their corresponding extension callbacks are executed for the @TestFactory
method but not for each dynamic test. In other words, if you access fields from the test instance within a lambda expression for a dynamic test, those fields will not be reset by callback methods or extensions between the execution of individual dynamic tests generated by the same @TestFactory
method.
我正在为我的(小)程序编写单元测试,测试用例在大约 30 个不同的文件中指定。
为了测试它,我只需要一个遍历所有文件、解析它们并执行所需内容的循环。
问题是,在那种情况下,我的所有测试都将被视为一个测试,因为它们都在同一个函数中,使用 @Test
表示法。
是否可以以某种方式拆分它,而不必为每个测试文件提供单独的功能?
将所有测试作为一个测试用例的问题是,我看不到哪个测试用例失败了;如果一个失败,其余的失败(我会得到 1 个测试失败而不是 5/30 失败)
我目前正在使用 JUnit (4.12),但我没有义务继续使用它,所以如果有更好的解决方案,我可以切换框架。
谢谢!
示例:
public class MyTests {
@Test
public void testFromFiles {
// loop through all the files
}
}
output: 1 test run successfully
更新: 所选答案对我来说非常有用,我添加了另一个 JUnit 5 解决方案(而不是 4),以防它对某人有所帮助。
试试这个方法:
@RunWith(Parameterized.class)
public class EdiTest {
@SuppressWarnings("WeakerAccess")
@Parameterized.Parameter(value = 0)
public String model;
@SuppressWarnings("WeakerAccess")
@Parameterized.Parameter(value = 1)
public String filename;
@Parameterized.Parameters(name = "{index}: testEDI({0}, {1})")
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][]{
{"753", "edi753_A.edi"},
{"753", "edi753_B.edi"},
{"754", "edi754.edi"},
{"810", "edi810-withTax.edi"},
{"810", "edi810-withoutTax.edi"},
});
}
@Before
public void setUpContext() throws Exception {
TestContextManager testContextManager = new TestContextManager(getClass());
testContextManager.prepareTestInstance(this);
}
@Test
public void testEDI() throws IOException {
String edi = IOUtils.toString(ClassLoader.getSystemResource(filename));
EdiConverter driver = ediConverterProvider.getConverter(model);
// your test code here
}
}
使用 JUnit 5 和 Dynamic Tests:
-
class DynamicTests { @TestFactory List<DynamicTest> createSomeTests() { return Arrays.asList( DynamicTest.dynamicTest("First dynamically created test", () -> assertTrue(true)), DynamicTest.dynamicTest("Second dynamically created test", () -> assertTrue(true)) ); } }
获取和过滤所有测试文件的示例:
class MyTestsClass { @TestFactory List<DynamicTest> runAllTestFiles() { List<DynamicTest> list = new ArrayList<DynamicTest>(); try (Stream<Path> paths = Files.walk(Paths.get("tests_dir"))) { List<Path> files = paths .filter(path -> path.getFileName().toString().endsWith(".mytests")) .collect(Collectors.toList()); files.forEach(file -> list.add( DynamicTest.dynamicTest( file.getFileName().toString(), () -> testFileWithSomeAsserts(file)) )); } catch (IOException e) { e.printStackTrace(); } return list; } }
@TestFactory
的工作方式与常规测试不同,在这种情况下无法使用 @BeforeEach
/ @AfterEach
:
Dynamic Test Lifecycle
The execution lifecycle of a dynamic test is quite different than it is for a standard
@Test
case. Specifically, there are no lifecycle callbacks for individual dynamic tests. This means that@BeforeEach
and@AfterEach
methods and their corresponding extension callbacks are executed for the@TestFactory
method but not for each dynamic test. In other words, if you access fields from the test instance within a lambda expression for a dynamic test, those fields will not be reset by callback methods or extensions between the execution of individual dynamic tests generated by the same@TestFactory
method.