Java 更改参数化测试的名称
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/650894/
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
Changing names of parameterized tests
提问by Epaga
Is there a way to set my own custom test case names when using parameterized tests in JUnit4?
在 JUnit4 中使用参数化测试时,有没有办法设置我自己的自定义测试用例名称?
I'd like to change the default — [Test class].runTest[n]
— to something meaningful.
我想将默认值 — [Test class].runTest[n]
—更改为有意义的内容。
采纳答案by rescdsk
This feature has made it into JUnit 4.11.
此功能已使其成为JUnit 4.11。
To use change the name of parameterized tests, you say:
要使用更改参数化测试的名称,您可以说:
@Parameters(name="namestring")
namestring
is a string, which can have the following special placeholders:
namestring
是一个字符串,它可以有以下特殊占位符:
{index}
- the index of this set of arguments. The defaultnamestring
is{index}
.{0}
- the first parameter value from this invocation of the test.{1}
- the second parameter value- and so on
{index}
- 这组参数的索引。默认namestring
值为{index}
.{0}
- 此测试调用的第一个参数值。{1}
- 第二个参数值- 等等
The final name of the test will be the name of the test method, followed by the namestring
in brackets, as shown below.
测试的最终名称将是测试方法的名称,后跟namestring
中括号,如下所示。
For example (adapted from the unit test for the Parameterized
annotation):
例如(改编自Parameterized
注释的单元测试):
@RunWith(Parameterized.class)
static public class FibonacciTest {
@Parameters( name = "{index}: fib({0})={1}" )
public static Iterable<Object[]> data() {
return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 },
{ 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } });
}
private final int fInput;
private final int fExpected;
public FibonacciTest(int input, int expected) {
fInput= input;
fExpected= expected;
}
@Test
public void testFib() {
assertEquals(fExpected, fib(fInput));
}
private int fib(int x) {
// TODO: actually calculate Fibonacci numbers
return 0;
}
}
will give names like testFib[1: fib(1)=1]
and testFib[4: fib(4)=3]
. (The testFib
part of the name is the method name of the @Test
).
会给出像testFib[1: fib(1)=1]
和这样的名字testFib[4: fib(4)=3]
。(testFib
名称的一部分是 的方法名称@Test
)。
回答by Yishai
Looking at JUnit 4.5, its runner clearly doesn't support that, as that logic is buried inside a private class inside the Parameterized class. You could not use the JUnit Parameterized runner, and create your own instead which would understand the concept of names (which leads to the question of how you might set a name ...).
看看 JUnit 4.5,它的运行器显然不支持它,因为该逻辑隐藏在 Parameterized 类中的私有类中。您不能使用 JUnit Parameterized runner,而是创建自己的来理解名称的概念(这会导致如何设置名称的问题......)。
From a JUnit perspective, it would be nice if instead of (or in addition to) just passing an increment, they would pass the comma delimited arguments. TestNG does this. If the feature is important to you, you can comment on the yahoo mailing list referenced at www.junit.org.
从 JUnit 的角度来看,如果不是(或除了)仅传递增量,而是传递逗号分隔的参数,那就太好了。TestNG 就是这样做的。如果该功能对您很重要,您可以在 www.junit.org 上引用的雅虎邮件列表上发表评论。
回答by Yishai
You can create a method like
您可以创建一个方法,如
@Test
public void name() {
Assert.assertEquals("", inboundFileName);
}
While I wouldn't use it all the time it would be useful to figure out exactly which test number 143 is.
虽然我不会一直使用它,但准确找出 143 是哪个测试编号会很有用。
回答by darrenp
I recently came across the same problem when using JUnit 4.3.1. I implemented a new class which extends Parameterized called LabelledParameterized. It has been tested using JUnit 4.3.1, 4.4 and 4.5. It reconstructs the Description instance using the String representation of the first argument of each parameter array from the @Parameters method. You can see the code for this at:
我最近在使用 JUnit 4.3.1 时遇到了同样的问题。我实现了一个新类,它扩展了 Parameterized,称为 LabelledParameterized。它已经使用 JUnit 4.3.1、4.4 和 4.5 进行了测试。它使用来自@Parameters 方法的每个参数数组的第一个参数的字符串表示来重建 Description 实例。您可以在以下位置查看代码:
http://code.google.com/p/migen/source/browse/trunk/java/src/.../LabelledParameterized.java?r=3789
http://code.google.com/p/migen/source/browse/trunk/java/src/.../LabelledParameterized.java?r=3789
and an example of its use at:
及其使用示例:
http://code.google.com/p/migen/source/browse/trunk/java/src/.../ServerBuilderTest.java?r=3789
http://code.google.com/p/migen/source/browse/trunk/java/src/.../ServerBuilderTest.java?r=3789
The test description formats nicely in Eclipse which is what I wanted since this makes failed tests a lot easier to find! I will probably further refine and document the classes over the next few days/weeks. Drop the '?' part of the URLs if you want the bleeding edge. :-)
Eclipse 中的测试描述格式很好,这正是我想要的,因为这使得失败的测试更容易找到!在接下来的几天/几周内,我可能会进一步完善和记录课程。删除“?” 如果你想要最前沿的 URLs 的一部分。:-)
To use it, all you have to do is copy that class (GPL v3), and change @RunWith(Parameterized.class) to @RunWith(LabelledParameterized.class) assuming the first element of your parameter list is a sensible label.
要使用它,您所要做的就是复制该类 (GPL v3),并将 @RunWith(Parameterized.class) 更改为 @RunWith(LabelledParameterized.class) 假设您的参数列表的第一个元素是一个合理的标签。
I don't know if any later releases of JUnit address this issue but even if they did, I can't update JUnit since all my co-developers would have to update too and we have higher priorities than re-tooling. Hence the work in the class to be compilable by multiple versions of JUnit.
我不知道 JUnit 的任何后续版本是否解决了这个问题,但即使他们解决了,我也无法更新 JUnit,因为我的所有合作开发人员也必须更新,而且我们比重新工具具有更高的优先级。因此,类中的工作可以由多个版本的 JUnit 编译。
Note:there is some reflection jiggery-pokery so that it runs across the different JUnit versions as listed above. The version specifically for JUnit 4.3.1 can be found hereand, for JUnit 4.4 and 4.5, here.
注意:有一些反射 jiggery-pokery,因此它可以跨上面列出的不同 JUnit 版本运行。可以在此处找到专门针对 JUnit 4.3.1 的版本,对于 JUnit 4.4 和 4.5,请在此处找到。
回答by binkley
I make extensive use of static import for Assert and friends, so it is easy for me to redefine assertion:
我为 Assert 和朋友广泛使用静态导入,所以我很容易重新定义断言:
private <T> void assertThat(final T actual, final Matcher<T> expected) {
Assert.assertThat(editThisToDisplaySomethingForYourDatum, actual, expected);
}
For example, you could add a "name" field to your test class, initialized in the constructor, and display that on test failure. Just pass it in as the first elements of your parameters array for each test. This also helps label the data:
例如,您可以向测试类添加一个“名称”字段,在构造函数中初始化,并在测试失败时显示该字段。只需将它作为参数数组的第一个元素传入每个测试。这也有助于标记数据:
public ExampleTest(final String testLabel, final int one, final int two) {
this.testLabel = testLabel;
// ...
}
@Parameters
public static Collection<Object[]> data() {
return asList(new Object[][]{
{"first test", 3, 4},
{"second test", 5, 6}
});
}
回答by David Moles
With Parameterized
as a model, I wrote my own custom test runner / suite -- only took about half an hour. It's slightly different from darrenp's LabelledParameterized
in that it lets you specify a name explicitly rather than relying on the first parameter's toString()
.
随着Parameterized
作为一种模式,我写我自己的自定义测试跑步/套件-只花了约半小时。它与 darrenp 的略有不同LabelledParameterized
,它允许您显式指定名称,而不是依赖于第一个参数的toString()
.
It also doesn't use arrays because I hate arrays. :)
它也不使用数组,因为我讨厌数组。:)
public class PolySuite extends Suite {
// //////////////////////////////
// Public helper interfaces
/**
* Annotation for a method which returns a {@link Configuration}
* to be injected into the test class constructor
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public static @interface Config {
}
public static interface Configuration {
int size();
Object getTestValue(int index);
String getTestName(int index);
}
// //////////////////////////////
// Fields
private final List<Runner> runners;
// //////////////////////////////
// Constructor
/**
* Only called reflectively. Do not use programmatically.
* @param c the test class
* @throws Throwable if something bad happens
*/
public PolySuite(Class<?> c) throws Throwable {
super(c, Collections.<Runner>emptyList());
TestClass testClass = getTestClass();
Class<?> jTestClass = testClass.getJavaClass();
Configuration configuration = getConfiguration(testClass);
List<Runner> runners = new ArrayList<Runner>();
for (int i = 0, size = configuration.size(); i < size; i++) {
SingleRunner runner = new SingleRunner(jTestClass, configuration.getTestValue(i), configuration.getTestName(i));
runners.add(runner);
}
this.runners = runners;
}
// //////////////////////////////
// Overrides
@Override
protected List<Runner> getChildren() {
return runners;
}
// //////////////////////////////
// Private
private Configuration getConfiguration(TestClass testClass) throws Throwable {
return (Configuration) getConfigMethod(testClass).invokeExplosively(null);
}
private FrameworkMethod getConfigMethod(TestClass testClass) {
List<FrameworkMethod> methods = testClass.getAnnotatedMethods(Config.class);
if (methods.isEmpty()) {
throw new IllegalStateException("@" + Config.class.getSimpleName() + " method not found");
}
if (methods.size() > 1) {
throw new IllegalStateException("Too many @" + Config.class.getSimpleName() + " methods");
}
FrameworkMethod method = methods.get(0);
int modifiers = method.getMethod().getModifiers();
if (!(Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
throw new IllegalStateException("@" + Config.class.getSimpleName() + " method \"" + method.getName() + "\" must be public static");
}
return method;
}
// //////////////////////////////
// Helper classes
private static class SingleRunner extends BlockJUnit4ClassRunner {
private final Object testVal;
private final String testName;
SingleRunner(Class<?> testClass, Object testVal, String testName) throws InitializationError {
super(testClass);
this.testVal = testVal;
this.testName = testName;
}
@Override
protected Object createTest() throws Exception {
return getTestClass().getOnlyConstructor().newInstance(testVal);
}
@Override
protected String getName() {
return testName;
}
@Override
protected String testName(FrameworkMethod method) {
return testName + ": " + method.getName();
}
@Override
protected void validateConstructor(List<Throwable> errors) {
validateOnlyOneConstructor(errors);
}
@Override
protected Statement classBlock(RunNotifier notifier) {
return childrenInvoker(notifier);
}
}
}
And an example:
和一个例子:
@RunWith(PolySuite.class)
public class PolySuiteExample {
// //////////////////////////////
// Fixture
@Config
public static Configuration getConfig() {
return new Configuration() {
@Override
public int size() {
return 10;
}
@Override
public Integer getTestValue(int index) {
return index * 2;
}
@Override
public String getTestName(int index) {
return "test" + index;
}
};
}
// //////////////////////////////
// Fields
private final int testVal;
// //////////////////////////////
// Constructor
public PolySuiteExample(int testVal) {
this.testVal = testVal;
}
// //////////////////////////////
// Test
@Ignore
@Test
public void odd() {
assertFalse(testVal % 2 == 0);
}
@Test
public void even() {
assertTrue(testVal % 2 == 0);
}
}
回答by yliang
from junit4.8.2, you can create your own MyParameterized class by simply copy Parameterized class. change the getName() and testName() methods in TestClassRunnerForParameters.
从 junit4.8.2 开始,您可以通过简单地复制参数化类来创建自己的 MyParameterized 类。更改 TestClassRunnerForParameters 中的 getName() 和 testName() 方法。
回答by Christian
None of it was working for me, so I got the source for Parameterized and modified it create a a new test runner. I didn't have to change much but IT WORKS!!!
没有一个对我有用,所以我得到了参数化的源代码并修改它创建一个新的测试运行器。我不需要改变太多,但它有效!!!
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.junit.Assert;
import org.junit.internal.runners.ClassRoadie;
import org.junit.internal.runners.CompositeRunner;
import org.junit.internal.runners.InitializationError;
import org.junit.internal.runners.JUnit4ClassRunner;
import org.junit.internal.runners.MethodValidator;
import org.junit.internal.runners.TestClass;
import org.junit.runner.notification.RunNotifier;
public class LabelledParameterized extends CompositeRunner {
static class TestClassRunnerForParameters extends JUnit4ClassRunner {
private final Object[] fParameters;
private final String fParameterFirstValue;
private final Constructor<?> fConstructor;
TestClassRunnerForParameters(TestClass testClass, Object[] parameters, int i) throws InitializationError {
super(testClass.getJavaClass()); // todo
fParameters = parameters;
if (parameters != null) {
fParameterFirstValue = Arrays.asList(parameters).toString();
} else {
fParameterFirstValue = String.valueOf(i);
}
fConstructor = getOnlyConstructor();
}
@Override
protected Object createTest() throws Exception {
return fConstructor.newInstance(fParameters);
}
@Override
protected String getName() {
return String.format("%s", fParameterFirstValue);
}
@Override
protected String testName(final Method method) {
return String.format("%s%s", method.getName(), fParameterFirstValue);
}
private Constructor<?> getOnlyConstructor() {
Constructor<?>[] constructors = getTestClass().getJavaClass().getConstructors();
Assert.assertEquals(1, constructors.length);
return constructors[0];
}
@Override
protected void validate() throws InitializationError {
// do nothing: validated before.
}
@Override
public void run(RunNotifier notifier) {
runMethods(notifier);
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public static @interface Parameters {
}
private final TestClass fTestClass;
public LabelledParameterized(Class<?> klass) throws Exception {
super(klass.getName());
fTestClass = new TestClass(klass);
MethodValidator methodValidator = new MethodValidator(fTestClass);
methodValidator.validateStaticMethods();
methodValidator.validateInstanceMethods();
methodValidator.assertValid();
int i = 0;
for (final Object each : getParametersList()) {
if (each instanceof Object[])
add(new TestClassRunnerForParameters(fTestClass, (Object[]) each, i++));
else
throw new Exception(String.format("%s.%s() must return a Collection of arrays.", fTestClass.getName(), getParametersMethod().getName()));
}
}
@Override
public void run(final RunNotifier notifier) {
new ClassRoadie(notifier, fTestClass, getDescription(), new Runnable() {
public void run() {
runChildren(notifier);
}
}).runProtected();
}
private Collection<?> getParametersList() throws IllegalAccessException, InvocationTargetException, Exception {
return (Collection<?>) getParametersMethod().invoke(null);
}
private Method getParametersMethod() throws Exception {
List<Method> methods = fTestClass.getAnnotatedMethods(Parameters.class);
for (Method each : methods) {
int modifiers = each.getModifiers();
if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))
return each;
}
throw new Exception("No public static parameters method on class " + getName());
}
public static Collection<Object[]> eachOne(Object... params) {
List<Object[]> results = new ArrayList<Object[]>();
for (Object param : params)
results.add(new Object[] { param });
return results;
}
}
回答by dsaff
You may also want to try JUnitParams: http://code.google.com/p/junitparams/
您可能还想尝试 JUnitParams:http: //code.google.com/p/junitparams/
回答by quarkonium
Check out JUnitParams as dsaff mentioned, works using ant to build parameterized test method descriptions in the html report.
查看 dsaff 提到的 JUnitParams,使用 ant 在 html 报告中构建参数化测试方法描述。
This was after trying LabelledParameterized and finding that it although it works with eclipse it does not work with ant as far as the html report is concerned.
这是在尝试 LabelledParameterized 并发现它虽然适用于 eclipse 之后,但就 html 报告而言,它不适用于 ant。
Cheers,
干杯,