Odpowiedzi:
Ta funkcja została wprowadzona do JUnit 4.11 .
Aby użyć zmień nazwę sparametryzowanych testów, powiedz:
@Parameters(name="namestring")
namestring
jest ciągiem, który może mieć następujące specjalne symbole zastępcze:
{index}
- indeks tego zestawu argumentów. Domyślnie namestring
jest to {index}
.{0}
- pierwsza wartość parametru z tego wywołania testu.{1}
- druga wartość parametruOstateczna nazwa testu to nazwa metody testowej, po której następuje namestring
w nawiasach, jak pokazano poniżej.
Na przykład (na podstawie testu jednostkowego Parameterized
adnotacji):
@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;
}
}
nada imiona takie jak testFib[1: fib(1)=1]
i testFib[4: fib(4)=3]
. ( testFib
Część nazwy to nazwa metody @Test
).
{0}
i {1}
są tablice? JUnit powinien idealnie zadzwonić Arrays.toString({0})
, a nie {0}.toString()
. Na przykład moja data()
metoda zwraca Arrays.asList(new Object[][] {{ new int[] { 1, 3, 2 }, new int[] { 1, 2, 3 } }});
.
Patrząc na JUnit 4.5, jego biegacz wyraźnie tego nie obsługuje, ponieważ logika ta jest zakopana w prywatnej klasie w klasie Sparametryzowanej. Nie można użyć narzędzia do parametryzacji JUnit i zamiast tego utworzyć własny, który zrozumiałby pojęcie nazw (co prowadzi do pytania, w jaki sposób można ustawić nazwę ...).
Z perspektywy JUnit byłoby miło, gdyby zamiast (lub oprócz) po prostu przekazać przyrost, przekazali argumenty rozdzielane przecinkami. TestNG to robi. Jeśli ta funkcja jest dla Ciebie ważna, możesz komentować listę dyskusyjną Yahoo, do której odwołuje się strona www.junit.org.
Ostatnio natknąłem się na ten sam problem podczas korzystania z JUnit 4.3.1. Zaimplementowałem nową klasę, która rozszerza Parameterized o nazwie LabelledParameterized. Został przetestowany przy użyciu JUnit 4.3.1, 4.4 i 4.5. Odtwarza instancję Description za pomocą reprezentacji ciągu pierwszego argumentu każdej tablicy parametrów z metody @Parameters. Możesz zobaczyć kod tego na:
http://code.google.com/p/migen/source/browse/trunk/java/src/.../LabelledParameterized.java?r=3789
i przykład jego zastosowania w:
http://code.google.com/p/migen/source/browse/trunk/java/src/.../ServerBuilderTest.java?r=3789
Opis testu ładnie formatuje się w Eclipse, co jest tym, czego chciałem, ponieważ dzięki temu nieudane testy są znacznie łatwiejsze do znalezienia! Prawdopodobnie będę dalej doskonalić i dokumentować zajęcia w ciągu najbliższych kilku dni / tygodni. Upuść '?' część adresów URL, jeśli chcesz mieć przewagę. :-)
Aby go użyć, wystarczy skopiować tę klasę (GPL v3) i zmienić @RunWith (Parameterized.class) na @RunWith (LabelledParameterized.class), zakładając, że pierwszy element listy parametrów jest sensowną etykietą.
Nie wiem, czy jakiekolwiek późniejsze wydania JUnit rozwiązują ten problem, ale nawet jeśli tak, nie mogę zaktualizować JUnit, ponieważ wszyscy moi programiści również musieliby zaktualizować i mamy wyższe priorytety niż ponowne oprzyrządowanie. Stąd praca w klasie, która ma być kompilowana przez wiele wersji JUnit.
Uwaga: istnieje pewien jiggery-pokery refleksji, dzięki czemu działa on w różnych wersjach JUnit wymienionych powyżej. Wersję specjalnie dla JUnit 4.3.1 można znaleźć tutaj, a dla JUnit 4.4 i 4.5 tutaj .
execute[0], execute[1] ... execute[n]
w wygenerowanych raportach z testów.
Dzięki Parameterized
za wzór, napisałem mój własny niestandardowy testy biegacz / apartament - tylko trwało około pół godziny. Różni się nieco od darrenpa LabelledParameterized
tym, że pozwala jawnie określać nazwę zamiast polegać na pierwszym parametrze toString()
.
Nie używa również tablic, ponieważ nienawidzę tablic. :)
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);
}
}
}
I przykład:
@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);
}
}
z junit4.8.2 możesz stworzyć własną klasę MyParameterized, po prostu kopiując Parameterized class. zmień metody getName () i testName () w TestClassRunnerForParameters.
Możesz również wypróbować JUnitParams: http://code.google.com/p/junitparams/
Możesz stworzyć metodę podobną do
@Test
public void name() {
Assert.assertEquals("", inboundFileName);
}
Chociaż nie używałbym go cały czas, dobrze byłoby dowiedzieć się, który test nr 143 jest.
W szerokim zakresie korzystam z importu statycznego dla Assert i przyjaciół, więc łatwo jest mi na nowo zdefiniować asercję:
private <T> void assertThat(final T actual, final Matcher<T> expected) {
Assert.assertThat(editThisToDisplaySomethingForYourDatum, actual, expected);
}
Na przykład możesz dodać pole „name” do swojej klasy testowej, zainicjowane w konstruktorze i wyświetlić je w przypadku niepowodzenia testu. Wystarczy przekazać go jako pierwsze elementy tablicy parametrów dla każdego testu. Pomaga to również oznaczyć dane:
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}
});
}
Nic z tego nie działało, więc dostałem źródło dla Sparametryzowanego i zmodyfikowałem go, tworząc nowy tester. Nie musiałem wiele zmieniać, ale TO DZIAŁA !!!
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;
}
}
Rozwiązaniem byłoby złapanie i zagnieżdżenie wszystkich Throwables w nowym Throwable z niestandardowym komunikatem, który zawiera wszystkie informacje o parametrach. Komunikat pojawi się w śladzie stosu. Działa to zawsze, gdy test kończy się niepowodzeniem dla wszystkich stwierdzeń, błędów i wyjątków, ponieważ wszystkie są podklasami Throwable.
Mój kod wygląda następująco:
@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);
}
}
}
Ślad stosu nieudanego testu to:
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
Sprawdź JUnitParams, jak wspomniano dsaff, działa przy użyciu ant, aby zbudować sparametryzowane opisy metod testowych w raporcie HTML.
Było to po wypróbowaniu LabelledParameterized i stwierdzeniu, że chociaż działa z zaćmieniem, nie działa z mrówką, jeśli chodzi o raport HTML.
Twoje zdrowie,
Ponieważ parametr, do którego uzyskano dostęp (np. Z "{0}"
zawsze zwraca toString()
reprezentację, jednym z obejść byłoby wykonanie anonimowej implementacji i zastąpienie toString()
w każdym przypadku. Na przykład:
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...
);
}