Yanıtlar:
Bu özellik JUnit 4.11 haline getirmiştir .
Parametreli testlerin adını değiştirmek için şunu söylersiniz:
@Parameters(name="namestring")
namestring
aşağıdaki özel yer tutuculara sahip olabilen bir dizedir:
{index}
- bu argüman kümesinin dizini. Varsayılan namestring
değer {index}
.{0}
- testin bu çağrılmasından ilk parametre değeri.{1}
- ikinci parametre değeriTestin son adı, namestring
aşağıda gösterildiği gibi test yönteminin adı ve ardından parantez içinde olacaktır.
Örneğin ( Parameterized
ek açıklama için birim testinden uyarlanmıştır ):
@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]
ve gibi isimler verecek testFib[4: fib(4)=3]
. ( testFib
Adın bir kısmı, öğesinin yöntem adıdır @Test
).
{0}
ve eğer {1}
? JUnit ideal olarak aramalı Arrays.toString({0})
, değil {0}.toString()
. Örneğin, data()
yöntemim geri döner Arrays.asList(new Object[][] {{ new int[] { 1, 3, 2 }, new int[] { 1, 2, 3 } }});
.
JUnit 4.5'e bakıldığında, koşucusu bunu desteklemez, çünkü bu mantık Parametrelenmiş sınıf içindeki özel bir sınıfın içine gömülür. JUnit Parametreli koşucuyu kullanamazsınız ve bunun yerine ad kavramını anlayacak olan kendi adınızı yaratamazsınız (bu da bir adı nasıl ayarlayabileceğiniz sorusuna yol açar ...).
Bir JUnit bakış açısından, sadece bir artışı geçmek yerine (veya buna ek olarak), virgülle sınırlanmış argümanları geçmeleri iyi olur. TestNG bunu yapar. Bu özellik sizin için önemliyse, www.junit.org adresinde belirtilen yahoo posta listesine yorum yapabilirsiniz.
Son zamanlarda JUnit 4.3.1 kullanırken aynı sorunla karşılaştım. LabelledParameterized adlı Parameterized genişleten yeni bir sınıf uyguladım. JUnit 4.3.1, 4.4 ve 4.5 kullanılarak test edilmiştir. @Parameters yöntemindeki her parametre dizisinin ilk bağımsız değişkeninin String temsilini kullanarak Description örneğini yeniden oluşturur. Bunun için kodu şu adreste görebilirsiniz:
http://code.google.com/p/migen/source/browse/trunk/java/src/.../LabelledParameterized.java?r=3789
ve kullanımının bir örneği:
http://code.google.com/p/migen/source/browse/trunk/java/src/.../ServerBuilderTest.java?r=3789
Test açıklaması Eclipse'de güzel bir şekilde formatlanıyor, bu da başarısız testlerin bulunmasını çok daha kolay hale getirdiğinden istediğim şey! Önümüzdeki birkaç gün / hafta boyunca muhtemelen sınıfları daha da hassaslaştıracağım ve belgeleyeceğim. '?' URL'lerin bir kısmını varsayalım. :-)
Bunu kullanmak için tek yapmanız gereken o sınıfı (GPL v3) kopyalamak ve parametre listenizdeki ilk öğenin mantıklı bir etiket olduğunu varsayarak @RunWith (Parameterized.class) değerini @RunWith (LabelledParameterized.class) olarak değiştirmek.
JUnit'in daha sonraki sürümlerinin bu sorunu ele alıp almadığını bilmiyorum ama yapsalar bile, tüm ortak geliştiricilerimin de güncellemesi gerekeceğinden ve yeniden takımlardan daha yüksek önceliklerimiz olduğundan JUnit'i güncelleyemiyorum. Bu nedenle, sınıftaki çalışma, JUnit'in birden çok sürümü tarafından derlenebilir.
Not: Yukarıda listelenen farklı JUnit sürümlerinde çalışması için bazı yansıma jiggery-pokery vardır. JUnit 4.3.1 için özel sürüm burada ve JUnit 4.4 ve 4.5 için burada bulunabilir .
execute[0], execute[1] ... execute[n]
Test raporları oluşturulan test raporlarında adlandırılır .
İle Parameterized
bir model olarak, kendi özel test koşucu / paketi yazdı - sadece yarım saat sürdü. Darrenp'lerden biraz farklıdır, çünkü LabelledParameterized
ilk parametreye güvenmek yerine açıkça bir ad belirtmenize izin verir toString()
.
Ayrıca diziler kullanmaz çünkü dizilerden nefret ederim. :)
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);
}
}
}
Ve bir örnek:
@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'den, sadece Parameterized sınıfını kopyalayarak kendi MyParameterized sınıfınızı oluşturabilirsiniz. TestClassRunnerForParameters içindeki getName () ve testName () yöntemlerini değiştirin.
Ayrıca JUnitParams'ı denemek isteyebilirsiniz: http://code.google.com/p/junitparams/
Gibi bir yöntem oluşturabilirsiniz
@Test
public void name() {
Assert.assertEquals("", inboundFileName);
}
Her zaman kullanmasam da, 143 numaralı testin tam olarak ne olduğunu bulmak faydalı olacaktır.
Assert ve arkadaşları için statik içe aktarmayı kapsamlı bir şekilde kullanıyorum, bu yüzden iddiayı yeniden tanımlamak benim için kolay:
private <T> void assertThat(final T actual, final Matcher<T> expected) {
Assert.assertThat(editThisToDisplaySomethingForYourDatum, actual, expected);
}
Örneğin, test sınıfınıza yapıcıda başlatılan bir "ad" alanı ekleyebilir ve bunu test başarısızlığında görüntüleyebilirsiniz. Her test için parametre dizinizin ilk öğeleri olarak iletmeniz yeterlidir. Bu ayrıca verilerin etiketlenmesine yardımcı olur:
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}
});
}
Hiçbiri benim için çalışmadı, bu yüzden Parametreli için kaynak aldım ve yeni bir test koşucusu oluşturmak için değiştirdim. Çok değiştirmek zorunda değildi ama işe yarıyor !!!
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;
}
}
Çözüm, tüm Throwable'ları yakalamak ve parametrelerle ilgili tüm bilgileri içeren özel bir iletiyle yeni bir Throwable'a yerleştirmek olacaktır. İleti yığın izlemesinde görünür. Bu, bir test, Throwable'ın tüm alt sınıfları olduğu için tüm iddialar, hatalar ve istisnalar için başarısız olduğunda çalışır.
Kodum şöyle görünüyor:
@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);
}
}
}
Başarısız olan testin yığın izlemesi:
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
Bahsedilen dsaff olarak JUnitParams göz atın, html raporunda parametreli test yöntemi açıklamaları oluşturmak için ant kullanarak çalışır.
Bu LabelledParameterized denedikten sonra ve tutulma ile çalışmasına rağmen html raporu söz konusu olduğunda karınca ile çalışmadığını bulduktan sonra oldu.
Alkış,
Parametre erişildiğinden (örneğin, "{0}"
her zaman toString()
temsili döndürür), geçici bir çözüm, anonim bir uygulama yapmak ve toString()
her durumda geçersiz kılmak olacaktır .
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...
);
}