Jawaban:
Fitur ini telah membuatnya menjadi JUnit 4.11 .
Untuk menggunakan ubah nama pengujian parameter, Anda mengatakan:
@Parameters(name="namestring")
namestring
adalah string, yang dapat memiliki penampung khusus berikut:
{index}
- indeks kumpulan argumen ini. Standarnya namestring
adalah{index}
.{0}
- nilai parameter pertama dari permohonan tes ini.{1}
- nilai parameter keduaNama akhir pengujian akan menjadi nama metode pengujian, diikuti oleh namestring
tanda kurung, seperti yang ditunjukkan di bawah ini.
Misalnya (diadaptasi dari tes unit untuk Parameterized
anotasi):
@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;
}
}
akan memberi nama seperti testFib[1: fib(1)=1]
dan testFib[4: fib(4)=3]
. (Bagian testFib
dari nama adalah nama metode dari @Test
).
{0}
dan {1}
apakah array? JUnit idealnya menelepon Arrays.toString({0})
, bukan {0}.toString()
. Sebagai contoh, data()
metode saya kembali Arrays.asList(new Object[][] {{ new int[] { 1, 3, 2 }, new int[] { 1, 2, 3 } }});
.
Melihat JUnit 4.5, pelarinya jelas tidak mendukung itu, karena logika itu terkubur di dalam kelas privat di dalam kelas Parameterisasi. Anda tidak dapat menggunakan pelari Parameterized JUnit, dan sebaliknya membuat sendiri yang akan memahami konsep nama (yang mengarah ke pertanyaan tentang bagaimana Anda dapat menetapkan nama ...).
Dari perspektif JUnit, alangkah baiknya jika alih-alih (atau sebagai tambahan) hanya melewati kenaikan, mereka akan melewati argumen yang dibatasi koma. TestNG melakukan ini. Jika fitur ini penting bagi Anda, Anda dapat mengomentari milis yahoo yang dirujuk di www.junit.org.
Saya baru-baru ini menemukan masalah yang sama ketika menggunakan JUnit 4.3.1. Saya menerapkan kelas baru yang memperluas Parameterized disebut LabelledParameterized. Ini telah diuji menggunakan JUnit 4.3.1, 4.4 dan 4.5. Itu merekonstruksi instance Deskripsi menggunakan representasi String argumen pertama dari setiap parameter array dari metode @ Parameter. Anda dapat melihat kode ini di:
http://code.google.com/p/migen/source/browse/trunk/java/src/.../LabelledParameterized.java?r=3789
dan contoh penggunaannya di:
http://code.google.com/p/migen/source/browse/trunk/java/src/.../ServerBuilderTest.java?r=3789
Uraian tes format bagus di Eclipse yang saya inginkan karena ini membuat tes gagal jauh lebih mudah ditemukan! Saya mungkin akan lebih memperbaiki dan mendokumentasikan kelas selama beberapa hari / minggu ke depan. Jatuhkan '?' bagian dari URL jika Anda ingin tepi berdarah. :-)
Untuk menggunakannya, yang harus Anda lakukan adalah menyalin kelas itu (GPL v3), dan mengubah @RunWith (Parameterized.class) menjadi @RunWith (LabelledParameterized.class) dengan asumsi elemen pertama dari daftar parameter Anda adalah label yang masuk akal.
Saya tidak tahu apakah ada rilis JUnit nanti yang mengatasi masalah ini, tetapi bahkan jika mereka melakukannya, saya tidak dapat memperbarui JUnit karena semua co-developer saya juga harus memperbarui dan kami memiliki prioritas yang lebih tinggi daripada menginstal ulang. Oleh karena itu pekerjaan di kelas dapat dikompilasi oleh beberapa versi JUnit.
Catatan: ada beberapa jiggery-pokery refleksi sehingga berjalan melintasi versi JUnit yang berbeda seperti yang tercantum di atas. Versi khusus untuk JUnit 4.3.1 dapat ditemukan di sini dan, untuk JUnit 4.4 dan 4.5, di sini .
execute[0], execute[1] ... execute[n]
dalam laporan pengujian yang dihasilkan.
Dengan Parameterized
sebagai model, saya menulis runner / suite test saya sendiri - hanya membutuhkan waktu sekitar setengah jam. Ini sedikit berbeda dari darrenp LabelledParameterized
karena memungkinkan Anda menentukan nama secara eksplisit daripada mengandalkan parameter pertama toString()
.
Itu juga tidak menggunakan array karena saya benci array. :)
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);
}
}
}
Dan sebuah contoh:
@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);
}
}
dari junit4.8.2, Anda dapat membuat kelas MyParameterized Anda sendiri hanya dengan menyalin kelas Parameterized. ubah metode getName () dan testName () di TestClassRunnerForParameters.
Anda mungkin juga ingin mencoba JUnitParams: http://code.google.com/p/junitparams/
Anda dapat membuat metode seperti
@Test
public void name() {
Assert.assertEquals("", inboundFileName);
}
Meskipun saya tidak akan menggunakannya sepanjang waktu, akan berguna untuk mengetahui dengan tepat nomor tes 143.
Saya banyak menggunakan impor statis untuk Assert dan teman-teman, jadi mudah bagi saya untuk mendefinisikan kembali pernyataan:
private <T> void assertThat(final T actual, final Matcher<T> expected) {
Assert.assertThat(editThisToDisplaySomethingForYourDatum, actual, expected);
}
Misalnya, Anda bisa menambahkan bidang "nama" ke kelas tes Anda, diinisialisasi dalam konstruktor, dan menampilkannya pada kegagalan tes. Cukup berikan sebagai elemen pertama array parameter Anda untuk setiap tes. Ini juga membantu memberi label pada 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}
});
}
Tidak ada yang bekerja untuk saya, jadi saya mendapatkan sumber untuk Parameter dan memodifikasinya membuat test runner baru. Saya tidak perlu banyak berubah tetapi ITU BEKERJA !!!
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;
}
}
Solusinya adalah menangkap dan menumpuk semua Throwables ke dalam Throwable baru dengan pesan khusus yang berisi semua informasi tentang parameter. Pesan akan muncul di jejak tumpukan. Ini bekerja setiap kali tes gagal untuk semua asersi, kesalahan, dan pengecualian karena semuanya adalah subclass dari Throwable.
Kode saya terlihat seperti ini:
@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);
}
}
}
Jejak tumpukan tes gagal adalah:
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
Lihat JUnitParams seperti yang disebutkan dsaff, bekerja menggunakan semut untuk membangun deskripsi metode pengujian parameter dalam laporan html.
Ini setelah mencoba LabelledParameterized dan menemukan bahwa itu meskipun bekerja dengan gerhana itu tidak bekerja dengan semut sejauh laporan html yang bersangkutan.
Bersulang,
Karena parameter diakses (misalnya dengan "{0}"
selalu mengembalikantoString()
representasi, satu solusi adalah membuat implementasi anonim dan menimpa toString()
dalam setiap kasus. Misalnya:
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...
);
}