在JUnit4中使用参数化测试时,是否有一种方法可以设置我自己的自定义测试用例名称?
我想改变默认的-[测试类]. runtest [n] -有意义的东西。
在JUnit4中使用参数化测试时,是否有一种方法可以设置我自己的自定义测试用例名称?
我想改变默认的-[测试类]. runtest [n] -有意义的东西。
当前回答
一种解决方法是捕获所有Throwable并将其嵌套到一个新的Throwable中,并使用包含有关参数的所有信息的自定义消息。消息将出现在堆栈跟踪中。 当所有断言、错误和异常的测试失败时,这种方法都有效,因为它们都是Throwable的子类。
我的代码是这样的:
@RunWith(Parameterized.class)
public class ParameterizedTest {
int parameter;
public ParameterizedTest(int parameter) {
super();
this.parameter = parameter;
}
@Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] { {1}, {2} });
}
@Test
public void test() throws Throwable {
try {
assertTrue(parameter%2==0);
}
catch(Throwable thrown) {
throw new Throwable("parameter="+parameter, thrown);
}
}
}
失败测试的堆栈跟踪是:
java.lang.Throwable: parameter=1
at sample.ParameterizedTest.test(ParameterizedTest.java:34)
Caused by: java.lang.AssertionError
at org.junit.Assert.fail(Assert.java:92)
at org.junit.Assert.assertTrue(Assert.java:43)
at org.junit.Assert.assertTrue(Assert.java:54)
at sample.ParameterizedTest.test(ParameterizedTest.java:31)
... 31 more
其他回答
这些都不适合我,所以我得到了Parameterized的源代码,并修改了它,创建了一个新的测试运行器。我不需要改变太多,但它工作!!
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;
}
}
我大量使用静态导入的Assert和朋友,所以我很容易重新定义断言:
private <T> void assertThat(final T actual, final Matcher<T> expected) {
Assert.assertThat(editThisToDisplaySomethingForYourDatum, actual, expected);
}
例如,您可以向您的测试类添加一个“name”字段,在构造函数中初始化,并在测试失败时显示它。只需将它作为每个测试参数数组的第一个元素传递进来。这也有助于标记数据:
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}
});
}
我最近在使用JUnit 4.3.1时遇到了同样的问题。我实现了一个新的类,它扩展了Parameterized,称为LabelledParameterized。它已经使用JUnit 4.3.1、4.4和4.5进行了测试。它使用@Parameters方法中每个参数数组的第一个参数的String表示形式重新构造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/.../ServerBuilderTest.java?r=3789
测试描述在Eclipse中的格式很好,这是我想要的,因为这使得失败的测试更容易找到!我可能会在接下来的几天/几周内进一步完善和记录这些类。去掉'?'部分的url,如果你想要最先进的。:-)
要使用它,您所要做的就是复制这个类(GPL v3),并假设参数列表的第一个元素是一个合理的标签,将@RunWith(Parameterized.class)更改为@RunWith(LabelledParameterized.class)。
我不知道JUnit的后续版本是否解决了这个问题,但即使他们解决了,我也不能更新JUnit,因为我的所有合作开发人员也必须更新,我们有比重新工具更重要的优先级。因此,类中的工作可以被多个版本的JUnit编译。
注意:这里有一些反射花招,这样它就可以在上面列出的不同JUnit版本之间运行。针对JUnit 4.3.1的版本可以在这里找到,针对JUnit 4.4和4.5的版本可以在这里找到。
参数化测试在内部调用toString()。 如果您创建覆盖toString()的对象包装器,它将改变测试的名称。
这里有一个例子,我在其他帖子中回答过。 https://stackoverflow.com/a/67023556/1839360
一种解决方法是捕获所有Throwable并将其嵌套到一个新的Throwable中,并使用包含有关参数的所有信息的自定义消息。消息将出现在堆栈跟踪中。 当所有断言、错误和异常的测试失败时,这种方法都有效,因为它们都是Throwable的子类。
我的代码是这样的:
@RunWith(Parameterized.class)
public class ParameterizedTest {
int parameter;
public ParameterizedTest(int parameter) {
super();
this.parameter = parameter;
}
@Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] { {1}, {2} });
}
@Test
public void test() throws Throwable {
try {
assertTrue(parameter%2==0);
}
catch(Throwable thrown) {
throw new Throwable("parameter="+parameter, thrown);
}
}
}
失败测试的堆栈跟踪是:
java.lang.Throwable: parameter=1
at sample.ParameterizedTest.test(ParameterizedTest.java:34)
Caused by: java.lang.AssertionError
at org.junit.Assert.fail(Assert.java:92)
at org.junit.Assert.assertTrue(Assert.java:43)
at org.junit.Assert.assertTrue(Assert.java:54)
at sample.ParameterizedTest.test(ParameterizedTest.java:31)
... 31 more