2009-03-16 7 views
162

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

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

+0

올바른 답변이므로 rescdsk의 새로운 대답을 받아 들일 수 있습니까? –

+0

예, 그냥 정확히 했어요. – Epaga

답변

233

이 기능은 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을 사용할 수있을 때 좋은 질문입니다 :-) –

+1

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

+2

예,하지만 버그가 있습니다. 이 게시에서 수행하는 것처럼 매개 변수 "name"값에 괄호를 넣으면 이클립스에서 단위 테스트 이름이 표시되지 않습니다. – djangofan

37

JUnit 4.5를 살펴보면, 해당 로직이 매개 변수화 된 클래스 내부의 개인 클래스 내부에 포함되어 있기 때문에 러너는 분명히 지원하지 않습니다. JUnit 매개 변수화 된 러너는 사용할 수 없으며 대신 이름 개념을 이해할 수있는 고유 한 이름을 만들 수 있습니다 (이름 설정 방법에 대한 의문이 생깁니다 ...).

JUnit의 관점에서 볼 때 증가분을 전달하는 대신 (또는 추가하여) 쉼표로 구분 된 인수를 전달하는 것이 좋습니다. TestNG가이 작업을 수행합니다. 이 기능이 중요하다면 www.junit.org에서 참조하는 야후 메일 링리스트에 대해 의견을 말할 수 있습니다. 내가 정확히 테스트 번호 143 인 알아 내기 위해 그것을 유용하게 사용될 수있는 모든 시간을 사용하지 않을 동안

+3

JUnit에서 개선 된 점이 있으면 고맙겠습니다! – guerda

+0

그것은 파머입니다! – Epaga

+1

아마도 테스트 클래스에서 toString()을 사용하는 인수를 전달하는 대신. 이 기능을 JUnit에 추가하는 것이 매우 간단 할 것이라고 생각합니다. – reccles

2

당신은

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

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

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

이 만드는 것이므로 내가 원하는 무엇을 잘 이클립스의 테스트 설명 형식 실패한 테스트를 훨씬 쉽게 찾을 수 있습니다! 다음 며칠/몇 주 동안 수업을 수정하고 문서화 할 것입니다. '?' 당신이 출혈 가장자리를 원한다면 URL의 일부. :-)

사용하려면 해당 클래스 (GPL v3)를 복사하고 매개 변수 목록의 첫 번째 요소를 가정하여 @RunWith (Parameterized.class)를 @RunWith (LabelledParameterized.class)로 변경하기 만하면됩니다. 의미있는 레이블입니다.

JUnit의 이후 릴리스에서이 문제를 해결할 수 있는지 알지 못하지만 그랬더라도 모든 공동 개발자가 업데이트해야하므로 JUnit을 업데이트 할 수 없으며 다시 툴링보다 우선 순위가 높습니다. . 따라서 클래스의 작업은 여러 버전의 JUnit에서 컴파일 할 수 있습니다.


참고 : 위에 나열된대로 다른의 JUnit 버전에서 실행되도록 일부 반사 속임수가있다. JUnit 4.3.1에 대한 버전은 here이고, JUnit 4.4 및 4.5의 경우 here입니다.

+0

입니다. 나는 그걸 살펴 봐야 할 것이다 ... – Epaga

+0

:-) 위의 메시지에서 가리킨 버전이 JUnit 4.3.1을 사용하기 때문에 오늘 내 공동 개발자 중 한 명은 문제가있다. 고 말했다. 그는 JUnit 4.5.0을 사용하고 있으며 문제가 발생했습니다. 나는 오늘이 문제를 해결할 것입니다. – darrenp

+1

그게 진짜 멋져요, 고마워요! – Aivar

2

나는 어설과 친구에 대한 고정 수입 광범위하게 사용, 그래서 나를 다시 정의하는 것이 주장 간단하다

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

예를 들어, 당신이 당신의 테스트 클래스에 "이름"필드를 추가 할 수 초기화 테스트 실패시 표시합니다.각 테스트에 대한 매개 변수 배열의 첫 번째 요소로 전달하십시오. 이것은 또한 데이터 레이블을하는 데 도움이 : 모델로 Parameterized

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

테스트에 어설 션이 실패하더라도 괜찮습니다.하지만 테스트에 실패한 예외가 발생하거나 테스트에서 예외가 발생할 것으로 예상되는 경우와 같은 다른 경우가 있습니다. 예를 들어 이름 오버 헤드를 고려해야합니다. 프레임 워크가 처리해야합니다. – Yishai

13

을, 난 내 자신의 사용자 정의 테스트 러너/제품군을 썼다 - 약 반 시간을했다. 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); 
    } 

} 
+0

와우! 좋은 직업 ... –

6

junit4.8.2에서, 당신이 당신의 자신의 MyParameterized 클래스를 만들 수 있습니다. TestClassRunnerForParameters에서 getName() 및 testName() 메소드를 변경하십시오.

+0

나는 이것을 시도했지만 도움이되지 않습니다. 새 클래스를 만드는 동안 getParametersMethod가 실패합니다. –

2

아무 것도 저에게 효과가 없었으므로 매개 변수화 된 소스를 얻었고 새로운 테스트 러너를 만들었습니다. 나는 많이 바꿀 필요가 없었지만 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; 
} 
} 
6

는 또한 JUnitParams을 시도 할 수 있습니다 : dsaff가 언급 한 바와 같이 http://code.google.com/p/junitparams/

+0

JUnit이 github으로 이동했습니다. 다음은 업데이트 된 링크입니다. https://github.com/Pragmatists/JUnitParams – rockydgeekgod

0

체크 아웃 JUnitParams는 HTML 보고서에서 매개 변수화 된 시험 방법 설명을 구축하는 개미를 사용하여 작동합니다.

이것은 LabelledParameterized를 시도한 후 발견되었습니다. eclipse에서 작동하지만 HTML 보고서와 관련하여 Ant에서는 작동하지 않습니다.

건배,

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

매개 변수에 액세스하기 때문에 (예를 들어 "{0}"와 항상 toString() 표현, 하나의 해결 방법을 반환 익명 구현을하고 각 경우에 toString()을 무시하는 것입니다 (예 :

).