Parametreli testlerin adlarını değiştirme


204

JUnit4'te parametreli testler kullanırken kendi özel test durum adlarımı ayarlamanın bir yolu var mı?

Varsayılan - [Test class].runTest[n]- değerini anlamlı bir şeyle değiştirmek istiyorum .

Yanıtlar:


300

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 namestringdeğer {index}.
  • {0} - testin bu çağrılmasından ilk parametre değeri.
  • {1} - ikinci parametre değeri
  • ve bunun gibi

Testin son adı, namestringaşağıda gösterildiği gibi test yönteminin adı ve ardından parantez içinde olacaktır.

Örneğin ( Parameterizedek 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]. ( testFibAdın bir kısmı, öğesinin yöntem adıdır @Test).


4
4.11'de olmamasının bir sebebi yok, usta. Şimdi 4.11 mevcut olduğunda, bu iyi bir soru :-)
Matthew Farwell

1
4.11 şimdi beta sürümündedir ve yukarıdaki ile aynı linkten indirilebilir :-)
rescdsk

2
Evet, ama bir hata var. Bu gönderide yaptığınız gibi "name" parametresine bir parantez koyarsanız, Eclipse'de birim test adının görüntülenmesini bozar.
djangofan

7
harika, ama ya diziler {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 } }});.
dogbane

1
@djangofan Bu 8 yaşında bir tutulma hatası: bugs.eclipse.org/bugs/show_bug.cgi?id=102512
Pool

37

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.


3
JUnit bu konuda bir gelişme varsa çok takdir ediyorum!
guerda

17
Yeni kontrol edildi, bunun için olağanüstü bir özellik isteği var: github.com/KentBeck/junit/issues#issue/44 Lütfen oy verin.
reccles

8
@Frank, bu konuyu ele alan sürümün henüz yayınlanmadığını düşünüyorum. JUnit 4.11'de olacak. O zaman (tasarımın aynı kaldığı varsayılarak), parametreleri isimler olarak almak da dahil olmak üzere testi nasıl adlandırdığınızı belirtmenin metinsel bir yolu hakkında olacaktır. Aslında çok hoş.
Yishai

5
JUnit 4.11 yayınlandı :-)
rescdsk

7
İşte gelecekteki referans için github.com/junit-team/junit/issues/44 orijinal sorunun güncellenmiş bağlantısı
kldavis4 26:13

20

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 .


:-) Yukarıdaki mesajda işaret ettiğim sürüm JUnit 4.3.1 (başlangıçta söylediğim gibi 4.4 değil) kullandığı için bugün ortak geliştiricilerimden biri ile bir sorun vardı. JUnit 4.5.0 kullanıyor ve sorunlara neden oldu. Bunlara bugün değineceğim.
darrenp

Sana gerektiğini anlamak biraz zaman aldı geçmesi kurucusundaki test adı değil ezberlemek onu. Kod için teşekkürler!
zürafa

Eclipse testlerini yaptığım sürece harika çalışıyor. Bununla birlikte, JUnit Ant Task ile çalışacak kimse var mı? execute[0], execute[1] ... execute[n]Test raporları oluşturulan test raporlarında adlandırılır .
Henrik Aasted Sørensen

Çok hoş. Tıkır tıkır çalışıyor. Bilgiyi ekleyebilirseniz, invoked @ Test yöntemine ilk parametre olarak "String label, ..." eklemeniz gerekir.
gia

13

İle Parameterizedbir model olarak, kendi özel test koşucu / paketi yazdı - sadece yarım saat sürdü. Darrenp'lerden biraz farklıdır, çünkü LabelledParameterizedilk 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);
  }

}

6

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.


Bunu denedim ama yardımcı olmuyor. Yeni sınıf oluştururken getParametersMethod başarısız olur.
java_enthu


2

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.


2

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}
    });
}

Test bir iddiayı geçemezse bu sorun olur, ancak testi geçemeyen bir istisna atılması veya testin bir istisna yapılmasını beklemesi durumunda, yapılması gereken adın düşünülmesini gerektiren başka durumlar da vardır. çerçeve tarafından ele alınmaktadır.
Yishai

2

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;
}
}

2

Çö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

0

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ış,


0

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...
    );
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.