[JAVA] Changements dans JUnit5M4-> M5

C'est ma première Qiita.

Taste of JUnit 5 au KANJAVA PARTY 2017 !!! tenu le 24 juin 2017 / junit5falsewei-jian) a été annoncé sous le titre. La version au moment de l'annonce était M4, mais il y a quelques changements dans M5, donc je vais vous suivre.

Cependant, puisque nous décrivons principalement JUnit Jupiter ici, veuillez vous référer au [Guide de l'utilisateur JUnit5] original (http://junit.org/junit5/docs/current/user-guide/) pour d'autres informations. Veuillez vous y référer.

Tous les fichiers jar ont reçu un attribut Manifest appelé "Automatic-Module-Name"

Il est compatible avec Jigsaw. Jigsaw n'est pas très familier avec cela, alors je vais l'éviter.

Le paramètre @ ParameterizedTest s'applique désormais uniquement aux méthodes de test

C'est un peu déroutant. Écrivez le code. Dans JUnit5M4, comme il y a un argument au moment du rappel de méthode de setup1, j'essaye d'appliquer le paramètre de @ ParameterrizedTest, mais cela échoue car les types ne correspondent pas. Au lieu de cela, j'ai pu recevoir les paramètres de ParameterizedTest avec la méthode de rappel du cycle de vie comme setup2. Dans JUnit5M5, le paramètre de @ ParameterizedTest n'est plus appliqué à la méthode de rappel du cycle de vie, donc une erreur se produira lorsque setup2 est rappelé. Cette correction vise à éviter des problèmes étranges lorsque @ ParameterizedTest et normal @ Test sont mélangés dans une classe de test.

ParameterizedNGTest.java


public class ParameterizedNGTest {
	@BeforeEach
	void setUp1(TestInfo info) {
	}

	@BeforeEach
	void setUp2(String p1, String p2) {
		System.out.println(p1 + p2);
	}
	
	@ParameterizedTest
	@CsvSource({ "x, 1", "y, 2","z, 3" })
	void testIt(String input, String expected) {
	}
}

Le nom de l'artefact "junit-jupiter-migration-support" a été remplacé par "junit-jupiter-migrationsupport"

Soyez prudent lors de l'acquisition avec Maven ou Gradle.

Les API suivantes ont changé

Si la classe ne change pas, le nom de la classe est omis.

Ancienne API Nouvelle API Remarques
ParameterResolver#supports() supportsParameter()
ParameterResolver#resolve() resolveParameter()
ContainerExecutionCondition#evaluate() ExecutionCondition#evaluateExecutionCondition() ContainerExecutionCondition est aboli
TestExecutionCondition#evaluate() ExecutionCondition#evaluateExecutionCondition() TestExecutionCondition est aboli
TestExtensionContext#getTestException() ExtensionContext#getExecutionException() TestExtensionContext est aboli
TestExtensionContext#getTestInstance() ExtensionContext#getTestInstance() TestExtensionContext est supprimé. La valeur de retour est également Object-> Optional<Object>Changer pour
TestTemplateInvocationContextProvider#supports() supportsTestTemplate()
TestTemplateInvocationContextProvider#resolve() provideTestTemplateInvocationContexts()
ArgumentsProvider#arguments() provideArguments()
ObjectArrayArguments#create() Arguments#of() ObjectArrayArguments obsolète
@MethodSource#names value

Introduit @ TestInstance

L'annotation @ TestInstance est une annotation qui contrôle le cycle de vie d'une classe de test. Jusqu'à présent, JUnit a matérialisé une classe de test à chaque fois qu'une méthode de test est exécutée.

TestInstancePerMethodTest.java


@RunWith(JUnitPlatform.class)
public class TestInstancePerMethodTest {
	private int i = 0;
	
	@Test
	void test1(){System.out.println(i++);}
	@Test
	void test2(){System.out.println(i++);}
}

Résultat d'exécution.


0
0

Si vous donnez @ TestInstance (et spécifiez Lifecycle.PER_CLASS), la classe de test ne sera matérialisée qu'une seule fois et le résultat sera différent.

TestInstancePerClassTest.java


@RunWith(JUnitPlatform.class)
@TestInstance(Lifecycle.PER_CLASS)
public class TestInstancePerClassTest {
	private int i = 0;
	
	@Test
	void test1(){System.out.println(i++);}
	@Test
	void test2(){System.out.println(i++);}
}

Résultat d'exécution.


0
1

Accorder @ TestInstance réduira le coût d'exécution de la classe de test, mais sachez que le processus d'initialisation doit être effectué dans @ BeforeEach, pas au moment de l'initialisation.

Dans le même temps, il convient de noter que @ BeforeAll et @ AfterAll doivent être spécifiés d'une manière qui correspond à la matérialisation de la classe de test, ce qui rend l'histoire un peu compliquée.

En d'autres termes, dans le cas de la spécification de PER_CLASS, une erreur d'exécution se produira à moins que «@ BeforeAll» ou «@ AfterAll» ne soit annoté pour la méthode dynamique au lieu de la méthode statique.

De plus, si vous utilisez @ TestInstance et @ Nested ensemble, vous pourrez écrire @ BeforeAll et @ AfterAll même pour les classes de test imbriquées, il est donc préférable de les utiliser correctement. C'est possible.

Au fait, si vous utilisez @ TestInstance, l'ordre des rappels du cycle de vie changera. CallBackOrderTest.java Le résultat de sortie quand est exécuté tel quel est comme indiqué dans ①, mais quand @ TestInstance est annoté, il est comme indiqué dans ②. Je pense que c'est un problème, mais si vous y réfléchissez normalement, c'est le résultat.

①.


ExecutionCondition:false
beforeAll
postProcessTestInstance
ExecutionCondition:true
beforeEach
beforeTestExecution
foo
handleTestExecutionException
afterTestExecution
afterEach
afterAll

②.


postProcessTestInstance
ExecutionCondition:false
beforeAll
ExecutionCondition:true
beforeEach
beforeTestExecution
foo
handleTestExecutionException
afterTestExecution
afterEach
afterAll

La spécification d'assertAll a changé

Jusqu'à JUnit5M4, si une exception se produisait dans chaque exécutable sans assertAll, les exécutables suivants n'étaient pas évalués et le test était traité comme une erreur, mais après M5, des exceptions autres que des exceptions de liste noire se produisaient. Mais l'évaluation n'est plus interrompue. Les résultats d'exécution de M4 et M5 avec le code suivant sont indiqués ci-dessous.

AssertAllInExceptionTest.java


@RunWith(JUnitPlatform.class)
public class AssertAllInExceptionTest {
	@Test
	void thrownNullPointerException() {
		assertAll(
				() -> {throw new NullPointerException();},
				() -> assertEquals(2, 3)
		);
	}
}
Résultat d'exécution de M4 Résultat d'exécution de M5
Capture d'écran 2017-07-13 13.09.51.png Capture d'écran 2017-07-13 13.14.23.png

Vous pouvez voir que M4 est évalué comme une erreur, M5 est évalué comme un échec et le deuxième exécutable est évalué.

Plus tard, j'ai utilisé le mot «exception de la liste noire» plus tôt, mais il y a une question qui dit «qu'est-ce que c'est!». Dans le Guide de l'utilisateur, le mot «exception sur liste noire» a été mentionné dans la partie de l'annonce du changement de spécification d'assertAll et un endroit plus tard dans la Release Note de M2.

If the exception is a blacklisted exception such as an OutOfMemoryError, however, it will be rethrown.

La réponse à la question "Qu'est-ce que tel que (etc.) !! Qu'y a-t-il d'autre !!" n'était pas dans le guide de l'utilisateur mais dans le code.

[BlacklistedExceptions.java](https://github.com/junit-team/junit5/blob/r5.0.0-M4/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ Autant que je puisse voir le code appelé BlacklistedExceptions.java), il semble qu'il ne s'agisse actuellement que d'OutOfMemoryError.

@ TestTemplate a été écrit

Il semble que ce soit de JUnit5M4, mais je l'ai oublié car il n'était pas écrit dans le guide de l'utilisateur. Il y avait une description dans le guide de l'utilisateur de JUnit5M5, donc j'écrirai un aperçu ici.

Je pense que «@ TestTemplate», comme «@ ParameterizedTest», sert à séparer le code de test et les données de test. J'ai découvert en lisant le code, mais comme @ ParameterizedTest est une annotation qui annote @ TestTemplate, je pense que @ TestTemplate ne devrait pas être un problème si vous n'êtes pas insatisfait de @ ParameterizedTest. Je vais.

Ce qui suit est une version légèrement modifiée de la traduction automatique du Guide de l'utilisateur.

3.14. Test Templates

La méthode @ TestTemplate est un modèle pour les cas de test, pas pour les cas de test réguliers. Par conséquent, il est conçu pour être appelé plusieurs fois, en fonction du nombre de contextes d'appel renvoyés par le fournisseur inscrit. Par conséquent, «@ TestTemplate» doit être utilisé avec une extension qui implémente TestTemplateInvocationContextProvider. L'appel d'une méthode de modèle de test se comporte comme une exécution de méthode @ Test normale et prend entièrement en charge les mêmes rappels et extensions de cycle de vie. Pour des exemples d'utilisation, consultez Fourniture de contextes d'appel pour les modèles de test.

5.8. Providing Invocation Contexts for Test Templates

La méthode @ TestTemplate ne peut être exécutée que si au moins un TestTemplateInvocationContextProvider est inscrit. Chacun de ces fournisseurs fournit un flux d'instances TestTemplateInvocationContext. Chaque contexte peut spécifier un nom d'affichage personnalisé et une liste d'extensions supplémentaires à utiliser uniquement lors du prochain appel à la méthode @ TestTemplate. L'exemple suivant décrit un modèle de test et montre comment inscrire et implémenter un TestTemplateInvocationContextProvider.

@TestTemplate
@ExtendWith(MyTestTemplateInvocationContextProvider.class)
void testTemplate(String parameter) {
    assertEquals(3, parameter.length());
}

static class MyTestTemplateInvocationContextProvider implements TestTemplateInvocationContextProvider {
    @Override
    public boolean supportsTestTemplate(ExtensionContext context) {
        return true;
    }

    @Override
    public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {
        return Stream.of(invocationContext("foo"), invocationContext("bar"));
    }

    private TestTemplateInvocationContext invocationContext(String parameter) {
        return new TestTemplateInvocationContext() {
            @Override
            public String getDisplayName(int invocationIndex) {
                return parameter;
            }

            @Override
            public List<Extension> getAdditionalExtensions() {
                return Collections.singletonList(new ParameterResolver() {
                    @Override
                    public boolean supportsParameter(ParameterContext parameterContext,
                            ExtensionContext extensionContext) {
                        return parameterContext.getParameter().getType().equals(String.class);
                    }

                    @Override
                    public Object resolveParameter(ParameterContext parameterContext,
                            ExtensionContext extensionContext) {
                        return parameter;
                    }
                });
            }
        };
    }
}

Dans cet exemple, le modèle de test est appelé deux fois. Le nom d'affichage de l'appel sera "foo" et "bar" spécifié dans le contexte de l'appel. Chaque appel enregistre un ParameterResolver personnalisé qui est utilisé pour résoudre les paramètres de méthode. Le résultat lors de l'utilisation de ConsoleLauncher est:

└─ testTemplate(String) ✔
   ├─ foo ✔
   └─ bar ✔

L'API d'extension TestTemplateInvocationContextProvider vous permet principalement de préparer des instances de classe de test séparément avec différents contextes, par exemple, des paramètres différents, ou de créer plusieurs fois sans changer le contexte. Tests en - Principalement utilisé pour implémenter divers tests qui reposent sur des appels répétés à des méthodes similaires.

Nom d'affichage rendu lisible par l'homme lorsque @ ParameterizedTest accepte un tableau comme argument

Peut-être en réponse à ce problème, si vous essayez simplement de générer un objet tableau en Java, [Ljava.lang.String; @ 7c29daf3 Je pense que j'ai corrigé le fait que je ne pouvais pas voir le contenu.

Cependant, je n'ai pas pu le vérifier car je ne pouvais pas écrire un petit code pouvant être comparé entre M4 et M5. Excusez-moi.

La spécification de @ EnumSource a changé

Dans M4, les noms de @ EnumSource spécifiaient le nom Enum à acquérir, mais avec l'ajout de la propriété appelée mode dans M5, la signification des informations passées aux noms peut être modifiée. C'était.

Mode Signification des noms
INCLUDE Valeur par défaut. S'applique uniquement au nom Enum spécifié dans les noms
EXCLUDE S'applique uniquement aux noms Enum non spécifiés dans les noms
MATCH_ALL S'applique uniquement aux noms Enum qui correspondent à toutes les expressions régulières spécifiées dans les noms
MATCH_ANY S'applique uniquement aux noms Enum qui correspondent à l'une des expressions régulières spécifiées dans les noms

(Solo) Je ne veux pas trop l'utiliser w

La spécification de @ MethodSource a changé

Spécifier DoubleStream, IntStream ou LongStream comme valeur de retour de la méthode spécifiée par @ MethodSource ne provoque plus d'erreur.

La spécification de @ TestFactory a changé

@ TestFactory prend désormais en charge tous les conteneurs dynamiques imbriqués. Pour plus de détails, voir Dynamic Container et le résumé DynamicNode. Voir junit5 / docs / current / api / org / junit / jupiter / api / DynamicNode.html).

C'est ça. Un exemple est également joint au guide de l'utilisateur.

    @TestFactory
    Stream<DynamicNode> dynamicTestsWithContainers() {
        return Stream.of("A", "B", "C")
            .map(input -> dynamicContainer("Container " + input, Stream.of(
                dynamicTest("not null", () -> assertNotNull(input)),
                dynamicContainer("properties", Stream.of(
                    dynamicTest("length > 0", () -> assertTrue(input.length() > 0)),
                    dynamicTest("not empty", () -> assertFalse(input.isEmpty()))
                ))
            )));
    }

En regardant cela, je suis devenu en quelque sorte capable de voir comment utiliser Dynamic Test. DynamicTest est-il un mécanisme qui facilite la traduction des tests écrits par un DSL externe en un ensemble structuré de code de test afin qu'il puisse être signalé correctement?

L'interface d'implémentation AfterAllCallback peut désormais intercepter les exceptions levées par @ BeforeAll.

À proprement parler, cela semble être une exception qui s'est produite dans la méthode annotant @ BeforeAll ou la méthode d'implémentation de l'interface BeforeAllCallback.

Je n'étais pas au courant, mais cela signifie que je ne pouvais pas le faire avec M4. Je l'ai recherché, mais dans M4, l'argument pour AfterAllCallback # afterAll était ContainerExtensionContext, et il n'y avait pas d'API pour obtenir l'exception qui s'est produite. À partir de M5, ContainerExtensionContext a été intégré dans ExtensionContedxt, afin qu'il puisse être capturé.

Dans la même théorie, les exceptions levées par @ BeforeEach peuvent être interceptées par l'interface d'implémentation AfterEachCallback. (Disponible sur M4)

Les informations peuvent désormais être partagées entre les classes de test via le Store

Ouais, je ne suis pas sûr.

Extensions may now share state across top-level test classes by using the Store of the newly introduced engine-level ExtensionContext.

Cependant, il y a trop peu d'explications sur le Store ...

Maturité de l'API

Seulement ceux qui sont différents / nouveaux de l'époque du document M4, mais ils sont comme suit.

Ancienne maturité Nouvelle maturité
@TestInstance - Non décrit
@TestTemplate - Experimental
Assertions#assertAll Maintained Experimental
Assumptions#assumingThat Maintained Experimental
DynamicContainer - Experimental
DynamicNode - Experimental
ExecutionCondition - Experimental
TestTemplateInvocationContextProvider - Experimental
TestTemplateInvocationContext - Experimental

Fondamentalement, il n'y a que des omissions ou des ajouts, et cela peut ne pas changer pour chaque jalon.

C'est comme ça?

En regardant les notes de publication du Guide de l'utilisateur, les principaux changements autres que le chapitre 7 sont les suivants.

finalement

Je n'ai pas eu beaucoup d'occasions d'annoncer récemment, donc je ferai de mon mieux pour JUnit 5. Nous espérons vous tenir au courant des changements, au moins jusqu'à la sortie officielle.

La prochaine étape est M6, mais à partir du 14 juillet 2017, sa sortie est prévue le 16 juillet 2017 et la progression semble être de 42%, elle sera donc probablement retardée w

Recommended Posts

Changements dans JUnit5M4-> M5
Changements dans Mockito 2
Changements dans mybatis-spring-boot-starter 2.0
Changements dans mybatis-spring-boot-starter 2.1
Changements dans mybatis-spring-boot-starter 1.3
Changements dans mybatis-spring-boot-starter 1.2
Modifications de l'emplacement principal dans iOS 14
Changements majeurs dans Spring Boot 1.5
Changements majeurs dans la fonctionnalité de base de Spring Framework 5.0