web-dev-qa-db-fra.com

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 afficherait "Le test actuel est testQuelque chose". 

Existe-t-il un moyen simple ou original de faire cela dans JUnit 4?

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

218
Dave Ray

JUnit 4.7 a ajouté cette fonctionnalité, semble-t-il, avec 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());
    }
}
350
FroMage

JUnit 4.9.x et supérieur

Depuis JUnit 4.9, la classe TestWatchman est obsolète au profit de la classe TestWatcher qui a l'invocation:

@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 permet d’imprimer 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());
   }
};
104
Duncan Jones

Dans JUnit 5, il existe une injection TestInfo qui simplifie les métadonnées de test fournies 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"));
}

Voir plus: JUnit 5 Guide de l'utilisateur , TestInfo javadoc .

8
Andrii Abramov

Pensez à utiliser SLF4J (Façade de journalisation simple pour Java) fournit quelques améliorations intéressantes en utilisant des messages paramétrés. La combinaison de SLF4J avec des 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() {

  }
}
7
journeyman

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

Vous pouvez ensuite 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 ajouter une annotation @RunWith (NameAwareRunner.class). Alternativement, vous pouvez mettre cette annotation sur une super-classe Test si vous ne voulez pas vous en rappeler à chaque fois. Ceci, bien sûr, limite votre sélection de coureurs mais cela peut être acceptable.

En outre, il faudra peut-être un peu de kung-fu pour obtenir le nom du test actuel hors du Runner et dans votre framework, mais cela vous donne au moins le nom.

6
chris.f.jones

Essayez ceci à la place:

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

La sortie ressemble à ceci:

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

REMARQUE: CeciNE FONCTIONNE PASsi votre test est une sous-classe de TestCase ! Le test s'exécute mais le code @Rule ne s'exécute jamais.

6
Yavin5

Sur la base du commentaire précédent et en tenant compte de cela, 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 de test 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!

3
Csaba Tenkes

JUnit 4 ne dispose pas de mécanisme prêt à l'emploi permettant à un scénario de test d'obtenir son propre nom (y compris lors de la configuration et du démontage).

3
cordellcp3
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) {
    }
}
2
jnorris
@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 );
    }
};
1
leojkelav

Dans JUnit 5 TestInfo se substitue à la règle TestName de JUnit 4. 

De la documentation: 

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

Pour récupérer le nom de la méthode du test exécuté en cours, vous avez deux options: String 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 être suffisant, 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éthodes dans @DisplayName("..") n’est pas une bonne idée. 

Vous pouvez également utiliser TestInfo.getTestMethod() qui retourne un objet Optional<Method>.
Si la méthode d'extraction 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());
}
0
davidxxx

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

Je vous suggérerais de découpler le nom de la méthode de test de votre ensemble de données 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 ensemble de données de test pour le cas de test. Associer les données de test au nom de la méthode de test suppose que les données de test ne peuvent être utilisées qu'une seule fois. Dans la plupart des cas, je suggère que les mêmes données de test soient utilisées dans plusieurs tests pour vérifier divers aspects de votre logique métier. 

0
emeraldjava