Java 具有动态测试数量的 JUnit 测试
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/358802/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
JUnit test with dynamic number of tests
提问by Hans-Peter St?rr
In our project I have several JUnittests that e.g. take every file from a directory and run a test on it. If I implement a testEveryFileInDirectory
method in the TestCase
this shows up as only one test that may fail or succeed. But I am interested in the results on each individual file. How can I write a TestCase
/ TestSuite
such that each file shows up as a separate test e.g. in the graphical TestRunner of Eclipse? (Coding an explicit test method for each file is not an option.)
在我们的项目中,我有几个JUnit测试,例如从目录中获取每个文件并对其运行测试。如果我实现了一个testEveryFileInDirectory
方法,TestCase
这将显示为只有一个可能失败或成功的测试。但我对每个单独文件的结果感兴趣。如何编写TestCase
/TestSuite
以便每个文件显示为单独的测试,例如在 Eclipse 的图形化 TestRunner 中?(为每个文件编写显式测试方法不是一种选择。)
Compare also the question ParameterizedTest with a name in Eclipse Testrunner.
采纳答案by bruno conde
Take a look at Parameterized Testsin JUnit 4.
查看JUnit 4中的参数化测试。
Actually I did this a few days ago. I'll try to explain ...
实际上,我几天前就这样做了。我会尽力解释...
First build your test class normally, as you where just testing with one input file. Decorate your class with:
首先正常构建您的测试类,就像您只使用一个输入文件进行测试一样。装饰你的班级:
@RunWith(Parameterized.class)
Build one constructor that takes the input that will change in every test call (in this case it may be the file itself)
构建一个构造函数,它接受将在每次测试调用中更改的输入(在这种情况下,它可能是文件本身)
Then, build a static method that will return a Collection
of arrays. Each array in the collection will contain the input arguments for your class constructor e.g. the file. Decorate this method with:
然后,构建一个将返回Collection
数组的静态方法。集合中的每个数组都将包含类构造函数的输入参数,例如文件。使用以下方法装饰此方法:
@Parameters
Here's a sample class.
这是一个示例类。
@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;
}
}
Also check this example
也检查这个例子
回答by Michael Borgwardt
Should be possible in JUnit 3 by inheriting from TestSuite
and overriding the tests()
method to list the files and for each return an instance of a subclass of TestCase
that takes the filename as constructor parameter and has a test method that tests the file given in the constructor.
在 JUnit 3 中应该可以通过继承TestSuite
和覆盖tests()
列出文件的方法,并为每个返回一个子类的实例,该子类TestCase
将文件名作为构造函数参数,并具有测试构造函数中给出的文件的测试方法。
In JUnit 4 it might be even easier.
在 JUnit 4 中,它可能更容易。
回答by McDowell
JUnit 3
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
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());
}
}
回答by Ben Hutchison
If TestNG is an option, you could use Parameters with DataProviders.
如果 TestNG 是一个选项,您可以将Parameters 与 DataProviders 一起使用。
Each individual file's test will have its result shown in the text-based report or Eclipse's TestNG plugin UI. The number of total tests run will count each of your files individually.
每个单独文件的测试结果都将显示在基于文本的报告或 Eclipse 的 TestNG 插件 UI 中。运行的总测试数将单独计算您的每个文件。
This behavior differs from JUnit Theories, in which all results are lumped under one "theory" entry and only count as 1 test. If you want separate result reporting in JUnit, you can try Parameterized Tests.
这种行为与 JUnit Theories不同,在JUnit Theories中,所有结果都集中在一个“理论”条目下,并且只计为 1 个测试。如果您想在 JUnit 中单独报告结果,您可以尝试Parameterized Tests。
Test and inputs
测试和输入
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
}
}
Example output
示例输出
PASSED: testFile(file1)
PASSED: testFile(file2)
===============================================
Default test
Tests run: 2, Failures: 0, Skips: 0
===============================================
回答by falsarella
You could consider using JUnitParams library, so you would have a few more (cleaner) options:
您可以考虑使用JUnitParams library,因此您将有更多(更干净)的选择:
@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);
}
}
You can see more samples of usage here.
In addition about JUnitParams, why writting parameterized tests with it is easier and more readable:
另外关于 JUnitParams,为什么用它编写参数化测试更容易和更易读:
JUnitParams project adds a new runner to JUnit and provides much easier and readable parametrised tests for JUnit >=4.6.
Main differences to standard JUnit Parametrised runner:
- more explicit - params are in test method params, not class fields
- less code - you don't need a constructor to set up parameters
- you can mix parametrised with non-parametrised methods in one class
- params can be passed as a CSV string or from a parameters provider class
- parameters provider class can have as many parameters providing methods as you want, so that you can group different cases
- you can have a test method that provides parameters (no external classes or statics anymore)
- you can see actual parameter values in your IDE (in JUnit's Parametrised it's only consecutive numbers of parameters)
JUnitParams 项目为 JUnit 添加了一个新的运行器,并为 JUnit >=4.6 提供了更简单易读的参数化测试。
与标准 JUnit 参数化运行器的主要区别:
- 更明确 - 参数在测试方法参数中,而不是类字段
- 更少的代码 - 您不需要构造函数来设置参数
- 你可以在一个类中混合参数化和非参数化方法
- params 可以作为 CSV 字符串或从参数提供程序类传递
- 参数提供者类可以有任意多的参数提供方法,这样你就可以对不同的情况进行分组
- 你可以有一个提供参数的测试方法(不再有外部类或静态)
- 您可以在 IDE 中看到实际的参数值(在 JUnit 的参数化中,它只是连续的参数数量)
回答by Kimble
I had a similar problem and ended up writing a simple JUnit 4 runner that allows med to dynamically generate tests.
我遇到了类似的问题,最终编写了一个简单的 JUnit 4 运行程序,它允许 med 动态生成测试。
回答by avandeursen
Junit 5 Parameterized Tests
Junit 5 参数化测试
JUnit 5parameterized tests support this by allowing the use of a method as data source:
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 动态测试
JUnit 5also supports this through the notion of a DynamicTest
, which is to be generated in a @TestFactory
, by means of the static method dynamicTest
.
JUnit 5还通过 a 的概念支持这一点,aDynamicTest
将@TestFactory
通过静态方法在 a 中生成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 */ }));
}
The tests run in your IDE (IntelliJ here) will be displayed like this:
在您的 IDE(此处为 IntelliJ)中运行的测试将显示如下: