Получить имя текущего выполняемого теста в JUnit 4

В JUnit 3 я мог получить имя текущего выполняемого теста следующим образом:

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

который напечатает «Текущий тест - это testSomething».

Есть ли какой-нибудь нестандартный или простой способ сделать это в JUnit 4?

Предыстория: очевидно, я не хочу просто печатать название теста. Я хочу загрузить данные, относящиеся к тесту, которые хранятся в ресурсе с тем же именем, что и тест. Вы знаете, соглашение важнее конфигурации и все такое.


person Dave Ray    schedule 23.01.2009    source источник
comment
Что дает приведенный выше код в JUnit 4?   -  person Bill the Lizard    schedule 23.01.2009
comment
Тесты JUnit 3 расширяют TestCase, где определено getName (). Тесты JUnit 4 не расширяют базовый класс, поэтому метод getName () вообще отсутствует.   -  person Dave Ray    schedule 23.01.2009
comment
У меня аналогичная проблема, когда я хочу ‹b› установить ‹/b› имя теста, поскольку я использую параметризованный бегун, который дает мне только пронумерованные тестовые примеры.   -  person Volker Stolz    schedule 16.02.2009
comment
Прекрасное решение с использованием Test или TestWatcher ... просто интересно (вслух), должно ли это когда-либо быть необходимо? Вы можете узнать, выполняется ли тест медленно, посмотрев на графики вывода времени, предоставленные Gradle. Вам никогда не нужно знать порядок, в котором работают тесты ...?   -  person mike rodent    schedule 23.10.2016


Ответы (14)


JUnit 4.7 добавил эту функцию, похоже, используя TestName-Rule. Похоже, это даст вам имя метода:

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());
    }
}
person FroMage    schedule 15.09.2009
comment
Также обратите внимание, что TestName недоступно в @before :( См.: old.nabble.com/ - person jm.; 05.11.2009
comment
По-видимому, более новые версии JUnit выполняются @Rule до @Before - я новичок в JUnit и без каких-либо трудностей зависел от TestName в моем @Before. - person MightyE; 16.04.2010
comment
comment
Если вы используете параметризованные тесты name.getMethodName () вернет {testA [0], testA [1] и т. Д.}, Я использую такие, как: assertTrue (name.getMethodName (). Match (testA (\ [\\ d \ ])?)); - person Legna; 28.05.2014
comment
@DuncanJones Почему предложенная альтернатива более эффективна? - person Stephan; 08.11.2018

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());
   }
};
person Duncan Jones    schedule 21.12.2012
comment
@takacsot Это удивительно. Не могли бы вы задать новый вопрос по этому поводу и отправить мне ссылку здесь? - person Duncan Jones; 21.01.2014
comment
Зачем использовать поле public? - person Raffi Khatchadourian; 19.03.2016
comment
@RaffiKhatchadourian См. stackoverflow.com/questions/14335558/ - person Duncan Jones; 26.03.2016

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.

person Andrii Abramov    schedule 25.12.2016

Попробуйте вместо этого:

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 никогда не запускается.

person Yavin5    schedule 19.04.2013
comment
Дай бог вам здоровья за вашу ЗАПИСЬ в самом примере. - person user655419; 29.09.2013
comment
Это НЕ работает - в данном случае - огурец игнорирует аннотации @Rule - person Benjineer; 01.12.2015

Подумайте об использовании SLF4J (Simple Logging Facade для 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() {

  }
}
person journeyman    schedule 29.12.2011

Сложный способ - создать свой собственный 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). В качестве альтернативы вы можете поместить эту аннотацию в суперкласс Test, если вы не хотите запоминать ее каждый раз. Это, конечно, ограничивает ваш выбор бегунов, но это может быть приемлемо.

Кроме того, может потребоваться немного кунг-фу, чтобы получить текущее имя теста из Runner и в ваш фреймворк, но это, по крайней мере, дает вам имя.

person chris.f.jones    schedule 16.04.2009
comment
По крайней мере, концептуально эта идея кажется мне довольно простой. Моя точка зрения: я бы не назвал это запутанным. - person user98761; 13.12.2012
comment
на суперклассе Test ... - Пожалуйста, хватит больше ужасных шаблонов проектирования, основанных на наследовании. Это так JUnit3! - person oberlies; 06.08.2013

В JUnit 4 нет готового механизма для того, чтобы тестовый пример мог получить собственное имя (в том числе во время установки и разборки).

person cordellcp3    schedule 23.01.2009
comment
Есть ли какой-нибудь нестандартный механизм, кроме проверки стека? - person Dave Ray; 23.01.2009
comment
Не тот случай, на который даны ответы ниже! может быть, назначить правильный ответ кому-нибудь другому? - person Toby; 04.04.2013

На основе предыдущего комментария и дальнейшего рассмотрения я создал расширение 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());
}
}

Наслаждаться!

person Csaba Tenkes    schedule 23.06.2016
comment
Привет, что это ImportUtilsTest, я получаю сообщение об ошибке, похоже, это класс регистратора, у меня есть дополнительная информация? Спасибо - person Sylhare; 08.06.2018
comment
Именованный класс - это просто пример тестового класса JUnit: пользователь JUnitHelper. Поправлю пример использования. - person Csaba Tenkes; 17.06.2018
comment
Ах, теперь я чувствую себя тупым, это было так очевидно. Большое спасибо! ;) - person Sylhare; 18.06.2018

Я предлагаю вам отделить имя метода тестирования от набора тестовых данных. Я бы смоделировал класс DataLoaderFactory, который загружает / кэширует наборы тестовых данных из ваших ресурсов, а затем в вашем тестовом примере cam вызывает некоторый метод интерфейса, который возвращает набор тестовых данных для тестового примера. Если тестовые данные привязаны к имени тестового метода, предполагается, что тестовые данные можно использовать только один раз, и в большинстве случаев я бы предположил, что одни и те же тестовые данные используются в нескольких тестах для проверки различных аспектов вашей бизнес-логики.

person emeraldjava    schedule 24.01.2009

Вы можете добиться этого, используя 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());
    }
};
person Coder    schedule 14.04.2015

В JUnit 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());
}
person davidxxx    schedule 12.08.2018

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

}
person silver    schedule 25.03.2020

У меня есть тестовый класс Junit4, который расширяет TestCase, поэтому пример с @Rule не работал (как упоминалось в других ответах).

Однако, если ваш класс расширяет TestCase, вы можете использовать getName () для получения текущего имени теста, чтобы это работало:

@Before
public void setUp() {
  System.out.println("Start test: " + getName());
}

@After
public void tearDown() {
  System.out.println("Finish test: " + getName());
}
person James Crawford    schedule 23.06.2021

person    schedule
comment
Я могу возразить, что он только хотел показать решение .. не понимаю, почему голосование против .... @downvoter: хотя бы, добавить полезную информацию .. - person Victor; 13.05.2015
comment
@skaffman Мы все любим видеть полный спектр альтернативных решений. Это самый близкий к тому, что я ищу: получение имени теста не непосредственно в тестовом классе, а в классе, который используется во время теста (например, где-то в компоненте регистратора). Там аннотации, относящиеся к тесту, больше не работают. - person Daniel Alder; 19.09.2017