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

В JUnit 3 я мог получить имя текущего запущенного теста, например:

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

который напечатал бы "Текущий тест - testSomething".

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

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

Ответ 1

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

Ответ 2

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

Ответ 3

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.

Ответ 4

Рассмотрите возможность использования 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() {

  }
}

Ответ 5

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

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

Ответ 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 и в вашу фреймворк, но это, по крайней мере, дает вам имя.

Ответ 7

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

Ответ 8

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

Ответ 9

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

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

Ответ 10

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

Ответ 11

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

Ответ 12

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

Ответ 13

В JUnit 5 TestInfo выступает в качестве замены для правила TestName из JUnit 4.

Из документации:

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

Чтобы получить имя метода текущего выполненного теста, у вас есть две опции: String TestInfo.getDisplayName() и Method TestInfo.getTestMethod().

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

В качестве альтернативы вы можете использовать TestInfo.getTestMethod() который возвращает Optional<Method> объект 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());
}