Obtenir le nom du test en cours d’exécution dans JUnit 4

Dans JUnit 3, je pourrais obtenir le nom du test en cours d’exécution comme ceci:

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

qui imprimerait “Test actuel est test quelque chose”.

Existe-t-il un moyen simple ou immédiat de le faire dans JUnit 4?

Contexte: Évidemment, je ne veux pas simplement imprimer le nom du test. Je souhaite charger des données spécifiques au test stockées dans une ressource portant le même nom que le test. Vous savez, convention sur la configuration et tout ça.

Merci!

JUnit 4.7 a ajouté cette fonctionnalité qui semble utiliser TestName-Rule . On dirait que cela vous donnera le nom de la méthode:

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

JUnit 4.9.x et supérieur

Depuis JUnit 4.9, la classe TestWatchman est déconseillée en faveur de la classe TestWatcher , qui est TestWatcher :

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

JUnit 4.7.x – 4.8.x

L’approche suivante imprimera les noms de méthode pour tous les tests d’une classe:

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

Envisagez d’utiliser SLF4J (Simple Logging Facade pour Java) pour obtenir des améliorations intéressantes en utilisant des messages paramétrés. La combinaison de SLF4J avec les implémentations de règles JUnit 4 peut fournir des techniques de journalisation de classe de test plus efficaces.

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

Une manière compliquée consiste à créer votre propre Runner en sous-classant org.junit.runners.BlockJUnit4ClassRunner.

Vous pouvez alors faire quelque chose comme ceci:

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

Ensuite, pour chaque classe de test, vous devrez append une annotation @RunWith (NameAwareRunner.class). Vous pouvez également placer cette annotation sur une superclasse Test si vous ne souhaitez pas vous en souvenir à chaque fois. Ceci, bien sûr, limite votre sélection de coureurs mais cela peut être acceptable.

De plus, cela peut prendre un peu de kung-fu pour obtenir le nom du test actuel du Runner et dans votre framework, mais cela vous donne au moins le nom.

Essayez plutôt ceci:

 public class MyTest { @Rule public TestName testName = new TestName(); @Rule public TestWatcher testWatcher = new TestWatcher() { @Override protected void starting(final Description description) { Ssortingng methodName = description.getMethodName(); Ssortingng className = description.getClassName(); className = className.subssortingng(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()); } } 

La sortie ressemble à ceci:

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

REMARQUE: Cela ne fonctionne PAS si votre test est une sous-classe de TestCase ! Le test s’exécute mais le code @Rule ne fonctionne jamais.

Dans JUnit 5, il existe une dependency injection qui simplifie la fourniture de métadonnées aux méthodes de test. Par exemple:

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

En savoir plus: JUnit 5 Guide de l’utilisateur , TestInfo javadoc .

JUnit 4 ne dispose d’aucun mécanisme prêt à l’emploi pour qu’un scénario de test puisse obtenir son propre nom (y compris lors de l’installation et du déassembly).

Basé sur le commentaire précédent et en considérant plus loin, j’ai créé une extension de TestWather que vous pouvez utiliser dans vos méthodes de test JUnit avec ceci:

 public class ImportUtilsTest { private static final Logger LOGGER = Logger.getLogger(ImportUtilsTest.class); @Rule public TestWatcher testWatcher = new JUnitHelper(LOGGER); @Test public test1(){ ... } } 

La classe d’assistance est la suivante:

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

Prendre plaisir!

 Ssortingng 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) { } } 
 @ClassRule public static TestRule watchman = new TestWatcher() { @Override protected void starting( final Description description ) { Ssortingng mN = description.getMethodName(); if ( mN == null ) { mN = "setUpBeforeClass.."; } final Ssortingng s = SsortingngTools.toSsortingng( "starting..JUnit-Test: %s.%s", description.getClassName(), mN ); System.err.println( s ); } }; 

Je vous suggère de découpler le nom de la méthode de test de votre dataset de test. Je modéliserais une classe DataLoaderFactory qui charge / met en cache les ensembles de données de test à partir de vos ressources, puis appelle dans votre cas de test une méthode d’interface qui renvoie un dataset de test pour le scénario de test. Les données de test associées au nom de la méthode de test supposent que les données de test ne peuvent être utilisées qu’une seule fois, alors que dans la plupart des cas, les mêmes données de test sont utilisées dans plusieurs tests pour vérifier les différents aspects de votre logique métier.

Vous pouvez y parvenir en utilisant Slf4j et 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()); } }; 

Dans JUnit 5, TestInfo agit en remplacement de la règle TestName de JUnit 4.

De la documentation:

TestInfo est utilisé pour injecter des informations sur le test ou le conteneur en cours dans les méthodes @Test, @RepeatedTest, @ParameterizedTest, @TestFactory, @BeforeEach, @AfterEach, @BeforeAll et @AfterAll.

Pour récupérer le nom de la méthode du test exécuté en cours, vous avez deux options: Ssortingng TestInfo.getDisplayName() et Method TestInfo.getTestMethod() .

Pour récupérer uniquement le nom de la méthode de test actuelle, TestInfo.getDisplayName() peut ne pas suffire, car le nom d’affichage par défaut de la méthode de test est methodName(TypeArg1, TypeArg2, ... TypeArg3) .
La duplication des noms de méthode dans @DisplayName("..") n’est pas nécessairement une bonne idée.

Comme alternative, vous pouvez utiliser TestInfo.getTestMethod() qui renvoie un object Optional .
Si la méthode de récupération est utilisée dans une méthode de test, vous n’avez même pas besoin de tester la valeur encapsulée 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()); }