매개 변수화 된 테스트 이름 변경


204

JUnit4에서 매개 변수화 된 테스트를 사용할 때 고유 한 사용자 정의 테스트 케이스 이름을 설정하는 방법이 있습니까?

기본값 [Test class].runTest[n]을 의미있는 것으로 변경하고 싶습니다 .

답변:


299

이 기능은 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.


4
그것이 4.11에없는 이유는 없습니다. 그것은 마스터입니다. 이제 4.11을 사용할 수있게되면 좋은 질문입니다 :-)
Matthew Farwell

1
4.11은 현재 베타 버전이며 위와 동일한 링크에서 다운로드 할 수 있습니다 :-)
rescdsk

2
네,하지만 버그가 있습니다. 이 게시에서하는 것처럼 매개 변수 "name"값에 괄호를 넣으면 Eclipse에서 단위 테스트 이름 표시가 중단됩니다.
djangofan

7
좋은,하지만 경우 {0}{1}배열은? JUnit은 이상적으로 호출 Arrays.toString({0})하지 않아야한다 {0}.toString(). 예를 들어 내 data()메서드는을 반환합니다 Arrays.asList(new Object[][] {{ new int[] { 1, 3, 2 }, new int[] { 1, 2, 3 } }});.
dogbane

1
@djangofan 이것은 8 년 된 이클립스 버그입니다 : bugs.eclipse.org/bugs/show_bug.cgi?id=102512
Pool

37

JUnit 4.5를 살펴보면 해당 러너는 로직이 Parameterized 클래스의 개인 클래스에 묻혀 있기 때문에이를 지원하지 않습니다. JUnit Parameterized 러너를 사용할 수 없으며 대신 이름의 개념을 이해하는 이름을 직접 만들 수 있습니다 (이름을 설정하는 방법에 대한 질문으로 이어짐).

JUnit 관점에서 증분을 전달하는 대신 쉼표로 구분 된 인수를 전달하는 것이 좋습니다. TestNG가이를 수행합니다. 이 기능이 중요한 경우 www.junit.org에서 참조하는 yahoo 메일 링리스트에 의견을 남길 수 있습니다.


3
JUnit에서 이것에 대한 개선이 있다면 대단히 감사하겠습니다!
guerda

17
방금 확인한 결과, github.com/KentBeck/junit/issues#issue/44에 대한 기능 요청이 있습니다. 투표 해주세요.
초에 reccles

8
@ Frank, 나는이 문제를 해결하는 릴리스가 아직 릴리스되지 않았다고 생각합니다. JUnit 4.11에 있습니다. 당시에는 (디자인이 동일하다고 가정) 매개 변수를 이름으로 사용하는 것을 포함하여 테스트 이름 지정 방법을 지정하는 텍스트 방식에 관한 것입니다. 사실 꽤 좋아요.
Yishai

5
JUnit을 4.11 지금 :-) 출시되었습니다
rescdsk

7
다음은 나중에 참조 할 수 있도록 원래 문제 github.com/junit-team/junit/issues/44에 대한 업데이트 된 링크입니다.
kldavis4

20

JUnit 4.3.1을 사용할 때 최근에 동일한 문제가 발생했습니다. LabelledParameterized라는 Parameterized를 확장하는 새로운 클래스를 구현했습니다. JUnit 4.3.1, 4.4 및 4.5를 사용하여 테스트되었습니다. @Parameters 메서드에서 각 매개 변수 배열의 첫 번째 인수에 대한 String 표현을 사용하여 Description 인스턴스를 재구성합니다. 이 코드는 다음에서 확인할 수 있습니다.

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 (Parameterized.class)를 @RunWith (LabelledParameterized.class)로 변경하면됩니다.

JUnit의 이후 릴리스 에서이 문제를 해결하는지 알 수는 없지만, 공동 개발자가 모두 업데이트해야하고 재 툴링보다 우선 순위가 높기 때문에 JUnit을 업데이트 할 수 없습니다. 따라서 클래스의 작업은 여러 버전의 JUnit에서 컴파일 할 수 있습니다.


참고 : 위에 나열된 것과 같이 다른 JUnit 버전에서 실행되도록 일부 리플렉션 jiggery-pokery가 있습니다. 구체적의 JUnit 4.3.1 버전이 발견 될 수있다 여기서 , JUnit을 4.4 및 4.5 그리고 여기 .


:-) 위의 메시지에서 가리키는 버전이 JUnit 4.3.1 (원래 말한 4.4가 아님)을 사용하기 때문에 오늘 공동 개발자 중 한 명이 문제가있었습니다. JUnit 4.5.0을 사용 중이며 문제가 발생했습니다. 나는 오늘 이것들을 다룰 것이다.
darrenp

생성자에서 테스트 이름 을 전달 해야 하지만 암기 하지 않아야 한다는 것을 이해하는 데 약간의 시간이 걸렸 습니다. 코드 감사합니다!
giraff

Eclipse에서 테스트를 실행하는 한 훌륭하게 작동합니다. 그래도 누구나 JUnit Ant Task에서 작동하도록 만든 경험이 있습니까? 테스트 보고서는 execute[0], execute[1] ... execute[n]생성 된 테스트 보고서에 이름이 지정 됩니다.
Henrik Aasted Sørensen

아주 좋아요 매력처럼 작동합니다. 정보를 추가 할 수 있으면 호출 된 @ Test-method에 첫 번째 매개 변수로 "문자열 레이블 ..."을 추가해야합니다.
gia

13

Parameterized모델로, 나는 내 자신의 사용자 정의 테스트 러너 / 제품군을 썼다 - 절반 밖에 시간 정도 걸렸다. darrenp와는 약간 다르 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);
  }

}

6

junit4.8.2부터는 단순히 Parameterized 클래스를 복사하여 MyParameterized 클래스를 작성할 수 있습니다. TestClassRunnerForParameters에서 getName () 및 testName () 메소드를 변경하십시오.


나는 이것을 시도했지만 도움이되지 않습니다. 새 클래스를 작성하는 중 getParametersMethod가 실패합니다.
java_enthu


2

당신은 같은 방법을 만들 수 있습니다

@Test
public void name() {
    Assert.assertEquals("", inboundFileName);
}

항상 사용하지는 않지만 정확히 143의 테스트 번호를 알아내는 것이 유용합니다.


2

Assert 및 친구들에게 정적 가져 오기를 광범위하게 사용하므로 어설 션을 재정의하기가 쉽습니다.

private <T> void assertThat(final T actual, final Matcher<T> expected) {
    Assert.assertThat(editThisToDisplaySomethingForYourDatum, actual, expected);
}

예를 들어, 테스트 클래스에 "name"필드를 추가하고 생성자에서 초기화하여 테스트 실패시이를 표시 할 수 있습니다. 각 테스트마다 매개 변수 배열의 첫 번째 요소로 전달하십시오. 또한 데이터 레이블을 지정하는 데 도움이됩니다.

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

테스트가 어설 션에 실패한 경우에는 문제가 없지만 테스트에 실패한 예외가 발생하거나 테스트에서 예외가 발생할 것으로 예상되는 경우 이름 오버 헤드를 고려해야하는 다른 경우가 있습니다. 프레임 워크에 의해 처리됩니다.
Yishai

2

그 중 어느 것도 나를 위해 일하지 않았으므로 Parameterized의 소스를 얻었고 수정하여 새로운 테스트 러너를 만들었습니다. 많이 바꿀 필요는 없었지만 IT는 효과가 있습니다 !!!

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

해결 방법은 모든 Throwable을 캐치하여 매개 변수에 대한 모든 정보를 포함하는 사용자 정의 메시지가있는 새 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

0

dsaff가 언급했듯이 JUnitParams를 확인하십시오. 개미를 사용하여 html 보고서에서 매개 변수가있는 테스트 방법 설명을 작성하십시오.

이것은 LabelledParameterized를 시도한 후 일식으로 작동하지만 html 보고서와 관련하여 개미와는 작동하지 않는다는 것을 알게되었습니다.

건배,


0

액세스 된 매개 변수 (예 : "{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...
    );
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.