Отримайте назву тесту, що виконується в JUnit 4


240

У JUnit 3 я міг би отримати назву тесту, який зараз працює:

public class MyTest extends TestCase
{
    public void testSomething()
    {
        System.out.println("Current test is " + getName());
        ...
    }
}

який надрукував би "Поточний тест - це тест-щось".

Чи є якийсь нестандартний чи простий спосіб зробити це в JUnit 4?

Передумови: Очевидно, я не хочу просто друкувати назву тесту. Я хочу завантажити дані, що стосуються тесту, які зберігаються в ресурсі з тим же найменуванням, що і тест. Ви знаєте, домовленості про конфігурацію та все таке.


Що дає вищевказаний код у JUnit 4?
Білл Ящірка

5
Тести JUnit 3 розширюють TestCase, де визначено getName (). Тести JUnit 4 не розширюють базовий клас, тому метод getName () взагалі не існує.
Дейв Рей

У мене є подібна проблема, коли я хочу <b> встановити </b> ім'я тесту, оскільки я використовую параметризований бігун, який дає мені лише пронумеровані тестові випадки.
Волкер Столц

Прекрасне рішення за допомогою Test або TestWatcher ... просто цікаво (вголос), чи не повинно колись у цьому бути необхідності? Ви можете дізнатись, чи працює тест повільно, переглянувши графіки виведення часу, задані Gradle. Вам ніколи не потрібно знати, в якому порядку працюють тести ...?
мійський гризун

Відповіді:


378

JUnit 4.7 додав цю функцію, здається, використовуючи правило TestName . Виглядає так, що ви отримаєте назву методу:

import org.junit.Rule;

public class NameRuleTest {
    @Rule public TestName name = new TestName();

    @Test public void testA() {
        assertEquals("testA", name.getMethodName());
    }

    @Test public void testB() {
        assertEquals("testB", name.getMethodName());
    }
}

4
Також зауважте, що TestName недоступний у @before :( Див .: old.nabble.com/…
jm.

41
Мабуть, новіші версії JUnit виконуються @Ruleраніше @Before- я новачок у JUnit і залежав від TestNameмене @Beforeбез особливих труднощів.
Могутній


2
Якщо ви використовуєте параметризовані тести, "name.getMethodName ()" повернеться {testA [0], testA [1] тощо), таким чином я використовую такі, як: assertTrue (name.getMethodName (). Match ("testA (\ [\ \ d \])? "));
Легна

2
@DuncanJones Чому запропонована альтернатива є "більш ефективною"?
Стефан

117

JUnit 4.9.x і вище

Починаючи з JUnit 4.9, TestWatchmanклас припинено на користь TestWatcherкласу, який має виклик:

@Rule
public TestRule watcher = new TestWatcher() {
   protected void starting(Description description) {
      System.out.println("Starting test: " + description.getMethodName());
   }
};

Примітка: Клас, що містить, повинен бути оголошений public.

JUnit 4.7.x - 4.8.x

Наступний підхід буде друкувати назви методів для всіх тестів у класі:

@Rule
public MethodRule watchman = new TestWatchman() {
   public void starting(FrameworkMethod method) {
      System.out.println("Starting test: " + method.getName());
   }
};

1
@takacsot Це дивно. Чи можете ви, будь ласка, опублікувати свіже запитання про це та надіслати мені посилання тут?
Дункан Джонс

Навіщо використовувати publicполе?
Раффі Хатчадуріан


16

JUnit 5 і вище

У JUnit 5 ви можете ввести, TestInfoщо спрощує мета-дані тестування, що надаються методам тестування. Наприклад:

@Test
@DisplayName("This is my test")
@Tag("It is my tag")
void test1(TestInfo testInfo) {
    assertEquals("This is my test", testInfo.getDisplayName());
    assertTrue(testInfo.getTags().contains("It is my tag"));
}

Дивіться більше: Посібник користувача JUnit 5 , TestInfo javadoc .


9

Спробуйте це замість цього:

public class MyTest {
        @Rule
        public TestName testName = new TestName();

        @Rule
        public TestWatcher testWatcher = new TestWatcher() {
            @Override
            protected void starting(final Description description) {
                String methodName = description.getMethodName();
                String className = description.getClassName();
                className = className.substring(className.lastIndexOf('.') + 1);
                System.err.println("Starting JUnit-test: " + className + " " + methodName);
            }
        };

        @Test
        public void testA() {
                assertEquals("testA", testName.getMethodName());
        }

        @Test
        public void testB() {
                assertEquals("testB", testName.getMethodName());
        }
}

Вихід виглядає приблизно так:

Starting JUnit-test: MyTest testA
Starting JUnit-test: MyTest testB

ПРИМІТКА. Це НЕ працює, якщо ваш тест є підкласом TestCase ! Тест запускається, але код @Rule просто ніколи не запускається.


3
Бог благословить вас на вашу ПРИМІТКУ на самому прикладі.
користувач655419

"Це НЕ працює" - приклад - огірок ігнорує анотації
@Rule

8

Поміркуйте, що використання SLF4J (Simple Logging Facade for Java) забезпечує деякі вдосконалені вдосконалення, використовуючи параметризовані повідомлення. Поєднання SLF4J з реалізацією правил JUnit 4 може забезпечити ефективніші методи ведення журналу тестового класу.

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.MethodRule;
import org.junit.rules.TestWatchman;
import org.junit.runners.model.FrameworkMethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LoggingTest {

  @Rule public MethodRule watchman = new TestWatchman() {
    public void starting(FrameworkMethod method) {
      logger.info("{} being run...", method.getName());
    }
  };

  final Logger logger =
    LoggerFactory.getLogger(LoggingTest.class);

  @Test
  public void testA() {

  }

  @Test
  public void testB() {

  }
}

6

Зведений спосіб - створити власного Runner шляхом підкласингу org.junit.runners.BlockJUnit4ClassRunner.

Потім ви можете зробити щось подібне:

public class NameAwareRunner extends BlockJUnit4ClassRunner {

    public NameAwareRunner(Class<?> aClass) throws InitializationError {
        super(aClass);
    }

    @Override
    protected Statement methodBlock(FrameworkMethod frameworkMethod) {
        System.err.println(frameworkMethod.getName());
        return super.methodBlock(frameworkMethod);
    }
}

Потім для кожного тестового класу потрібно буде додати примітку @RunWith (NameAwareRunner.class). Крім того, ви можете помістити цю анотацію в тестовий надклас, якщо ви не хочете її пам'ятати кожен раз. Це, звичайно, обмежує ваш вибір бігунів, але це може бути прийнятним.

Крім того, може знадобитися трохи кунг-фу, щоб дістати поточне ім'я тесту з Runner і у ваші рамки, але це, принаймні, отримає вам ім'я.


Принаймні, концептуально ця ідея мені здається досить простою. Моя думка: я б не назвав це звивистим.
user98761

"на тестовому суперкласі ..." - Будь ласка, більше немає жахливих моделей дизайну, заснованих на спадщині. Це так JUnit3!
oberlies

3

JUnit 4 не має жодного позасистемного механізму для тестового випадку, щоб отримати своє власне ім’я (у тому числі під час налаштування та після виходу із системи).


1
Чи існує там поза механізм, який не використовується, окрім огляду стека?
Дейв Рей

4
Не так, якщо відповіді наведені нижче! може, призначити правильну відповідь комусь іншому?
Тобі

3
String testName = null;
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
for (int i = trace.length - 1; i > 0; --i) {
    StackTraceElement ste = trace[i];
    try {
        Class<?> cls = Class.forName(ste.getClassName());
        Method method = cls.getDeclaredMethod(ste.getMethodName());
        Test annotation = method.getAnnotation(Test.class);
        if (annotation != null) {
            testName = ste.getClassName() + "." + ste.getMethodName();
            break;
        }
    } catch (ClassNotFoundException e) {
    } catch (NoSuchMethodException e) {
    } catch (SecurityException e) {
    }
}

1
Я можу стверджувати , що він тільки хотів показати рішення .. не розумію , чому негативний голос .... @downvoter: по крайней мере, по крайней мере, додати корисну інформацію ..
Віктор

1
@skaffman Всі ми любимо бачити повний спектр альтернативних рішень. Це найближче для того, що я шукаю: Отримання імені тесту не безпосередньо в тестовому класі, а в класі, який використовується під час тесту (наприклад, десь у складі журналу). Там примітки, що стосуються тесту, більше не працюють.
Даніель Алдер

3

На основі попереднього коментаря та подальшого врахування я створив розширення TestWather, яке ви можете використовувати у своїх методах тестування JUnit при цьому:

public class ImportUtilsTest {
    private static final Logger LOGGER = Logger.getLogger(ImportUtilsTest.class);

    @Rule
    public TestWatcher testWatcher = new JUnitHelper(LOGGER);

    @Test
    public test1(){
    ...
    }
}

Наступний клас помічників тесту:

public class JUnitHelper extends TestWatcher {
private Logger LOGGER;

public JUnitHelper(Logger LOGGER) {
    this.LOGGER = LOGGER;
}

@Override
protected void starting(final Description description) {
    LOGGER.info("STARTED " + description.getMethodName());
}

@Override
protected void succeeded(Description description) {
    LOGGER.info("SUCCESSFUL " + description.getMethodName());
}

@Override
protected void failed(Throwable e, Description description) {
    LOGGER.error("FAILURE " + description.getMethodName());
}
}

Насолоджуйтесь!


Привіт, що це ImportUtilsTest, я отримую помилку, здається, це клас реєстратора, чи є у мене більше інформації? Спасибі
Sylhare

1
Названий клас - лише приклад тестового класу JUnit: користувача JUnitHelper. Я виправлю приклад використання.
Csaba Tenkes

А тепер я почуваюсь німим, це було так очевидно. Дуже дякую! ;)
Sylhare

1
@ClassRule
public static TestRule watchman = new TestWatcher() {
    @Override
    protected void starting( final Description description ) {
        String mN = description.getMethodName();
        if ( mN == null ) {
            mN = "setUpBeforeClass..";
        }

        final String s = StringTools.toString( "starting..JUnit-Test: %s.%s", description.getClassName(), mN );
        System.err.println( s );
    }
};

0

Я б запропонував вам відокремити назву методу тестування з набору даних тесту. Я б моделював клас DataLoaderFactory, який завантажує / кешує набори тестових даних з ваших ресурсів, а потім у вашому тестовому випадку викликує якийсь метод інтерфейсу, який повертає набір тестових даних для тестового випадку. Якщо дані про тест прив’язані до назви методу тестування, припускають, що дані тестування можуть бути використані лише один раз, де в більшості випадків я б запропонував, що ті самі дані тестування використовуються в декількох тестах для перевірки різних аспектів вашої бізнес-логіки.


0

Ви можете досягти цього за допомогою Slf4jіTestWatcher

private static Logger _log = LoggerFactory.getLogger(SampleTest.class.getName());

@Rule
public TestWatcher watchman = new TestWatcher() {
    @Override
    public void starting(final Description method) {
        _log.info("being run..." + method.getMethodName());
    }
};

0

У ЮН 5 TestInfo діє замінна для правила TestName від JUnit 4.

З документації:

TestInfo використовується для введення інформації про поточний тест або контейнер до методів @Test, @RepeatedTest, @ParameterizedTest, @TestFactory, @BeforeEach, @AfterEach, @BeforeAll та @AfterAll.

Для отримання назви методу поточного виконаного тесту у вас є два варіанти: String TestInfo.getDisplayName()і Method TestInfo.getTestMethod().

Для отримання лише імені поточного методу тестування TestInfo.getDisplayName()може бути недостатньо, оскільки відображуване ім'я методу тестування за замовчуванням methodName(TypeArg1, TypeArg2, ... TypeArg3).
Дублювання імен методів у @DisplayName("..")не потрібна хороша ідея.

В якості альтернативи ви можете використовувати TestInfo.getTestMethod()цей повернення Optional<Method>об'єкта.
Якщо метод пошуку використовується в тестовому методі, вам навіть не потрібно тестувати Optionalобернене значення.

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.Test;

@Test
void doThat(TestInfo testInfo) throws Exception {
    Assertions.assertEquals("doThat(TestInfo)",testInfo.getDisplayName());
    Assertions.assertEquals("doThat",testInfo.getTestMethod().get().getName());
}

0

JUnit 5 через ExtensionContext

Перевага:

Ви можете мати додаткові функціональні можливості ExtensionContextшляхом переосмислення afterEach(ExtensionContext context).

public abstract class BaseTest {

    protected WebDriver driver;

    @RegisterExtension
    AfterEachExtension afterEachExtension = new AfterEachExtension();

    @BeforeEach
    public void beforeEach() {
        // Initialise driver
    }

    @AfterEach
    public void afterEach() {
        afterEachExtension.setDriver(driver);
    }

}
public class AfterEachExtension implements AfterEachCallback {

    private WebDriver driver;

    public void setDriver(WebDriver driver) {
        this.driver = driver;
    }

    @Override
    public void afterEach(ExtensionContext context) {
        String testMethodName = context.getTestMethod().orElseThrow().getName();
        // Attach test steps, attach scsreenshots on failure only, etc.
        driver.quit();
    }

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.