Изменение имен параметризованных тестов
есть ли способ установить мои собственные имена тестовых наборов при использовании параметризованных тестов в JUnit4?
Я хотел бы изменить значение по умолчанию -[Test class].runTest[n]
- к чему-то значимому.
12 ответов:
эта функция превратила его в JUnit 4.11.
чтобы использовать изменить имя параметризованных тестов, вы говорите:
@Parameters(name="namestring")
namestring
- это строка, которая может содержать следующие специальные заполнители:
{index}
- индекс этого набора аргументов. Значение по умолчаниюnamestring
и{index}
.{0}
- первое значение параметра из этого вызова тест.{1}
- значение второго параметра- и так далее
конечным именем теста будет имя метода теста, за которым следует
namestring
в скобках, как показано ниже.например (адаптировано из модульного теста для
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; } }
дам имена, как
testFib[1: fib(1)=1]
иtestFib[4: fib(4)=3]
. (ТегtestFib
часть имени-это имя метода@Test
).
глядя на JUnit 4.5, его бегун явно не поддерживает это, поскольку эта логика похоронена внутри частного класса внутри параметризованного класса. Вы не можете использовать параметризованный бегун JUnit и вместо этого создать свой собственный, который будет понимать концепцию имен (что приводит к вопросу о том, как вы можете установить имя ...).
с точки зрения JUnit было бы неплохо, если бы вместо (или в дополнение к) просто передавая инкремент, они передавали бы запятую аргументы. TestNG делает это. Если эта функция важна для вас, вы можете прокомментировать список рассылки yahoo, на который ссылается www.junit.org.
недавно я столкнулся с той же проблемой при использовании JUnit 4.3.1. Я реализовал новый класс, который расширяет параметризованный называется LabelledParameterized. Он был протестирован с помощью JUnit 4.3.1, 4.4 и 4.5. Он восстанавливает экземпляр описания, используя строковое представление первого аргумента каждого массива параметров из метода @Parameters. Вы можете увидеть код для этого в:
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(параметризованный.класс) в @RunWith (LabelledParameterized.class) предполагая, что первый элемент вашего списка параметров является разумной меткой.
Я не знаю, будут ли какие-либо более поздние версии JUnit решать эту проблему, но даже если бы они это сделали, я не могу обновить JUnit, так как все мои со-разработчики должны были бы обновление тоже, и у нас есть более высокие приоритеты, чем переоснащение. Следовательно, работа в классе будет компилироваться несколькими версиями JUnit.
Примечание: есть некоторое отражение jiggery-pokery, так что он работает через различные версии JUnit, как указано выше. Версия специально для JUnit 4.3.1 можно найти здесь, здесь.
С
Parameterized
в качестве модели я написал свой собственный пользовательский тестовый раннер / набор - всего около получаса. Это немного отличается от дарренпаLabelledParameterized
в том, что он позволяет указать имя явно, а не полагаться на первый параметрtoString()
.Он также не использует массивы, потому что я ненавижу массивы. :)
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); } } }
и пример:
@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); } }
из junit4.8.2 Вы можете создать свой собственный класс MyParameterized, просто скопировав параметризованный класс. измените методы getName() и testName() в TestClassRunnerForParameters.
вы также можете попробовать JUnitParams:http://code.google.com/p/junitparams/
вы можете создать такой метод, как
@Test public void name() { Assert.assertEquals("", inboundFileName); }
хотя я бы не использовал его все время, было бы полезно выяснить, какой именно тест номер 143.
Я широко использую статический импорт для Assert и друзей, поэтому мне легко переопределить утверждение:
private <T> void assertThat(final T actual, final Matcher<T> expected) { Assert.assertThat(editThisToDisplaySomethingForYourDatum, actual, expected); }
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} }); }
ничего из этого не работало для меня, поэтому я получил источник для параметризации и изменил его, создав новый тестовый бегун. Мне не пришлось много менять, но это работает!!!
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; } }
обходным путем было бы поймать и вложить все Throwables в новый 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
Проверьте JUnitParams как упоминалось в dsaff, работает с использованием ant для построения параметризованных описаний методов тестирования в отчете html.
Это было после попытки LabelledParameterized и найти, что он, хотя он работает с eclipse, он не работает с ant, насколько это касается отчета html.
спасибо,
С момента обращения к параметру (например, с
"{0}"
всегда возвращаетtoString()
представление, одним из обходных путей было бы сделать анонимную реализацию и переопределитьtoString()
в каждом случае. Например:public static Iterable<? extends Object> data() { return Arrays.asList( new MyObject(myParams...) {public String toString(){return "my custom test name";}}, new MyObject(myParams...) {public String toString(){return "my other custom test name";}}, //etc... ); }