[JAVA] Introduction au test de paramétrage dans JUnit

Aperçu

Indépendamment de JUnit, le code de configuration de l'appareil dans le code de test est Il a tendance à être long et réduit la lisibilité.

Dans cet article, nous nous concentrons sur la «valeur d'entrée» et la «valeur attendue» des éléments du dispositif de test. Je vais résumer la méthode de description de ces préparations proprement par paramétrage.

Paramétrage des valeurs d'entrée et attendues

Les quatre étapes suivantes sont nécessaires pour le test de paramétrage de JUnit.

  1. Donnez au lanceur de test Théories de la classe de test
  2. Ajoutez l'annotation Theory au scénario de test et spécifiez les paramètres à recevoir. (Si vous n'effectuez pas l'étape 4, toutes les combinaisons de paramètres spécifiés seront exécutées)
  3. Préparez les paramètres
  4. Définissez une combinaison de paramètres qui ne seront pas exécutés

1. 1. Testeur de théories

Si vous souhaitez effectuer un test paramétré, placez l'annotation RunWith dans la classe de test Spécifiez la classe org.junit.experimental.theories.Theories. Le lanceur de test Teories peut être utilisé avec le lanceur de test inclus.

@RunWith(Enclosed.class)
public class ParameterizedTest {

    @RunWith(Theories.class)
public static class Pour les nombres{
    }

    @RunWith(Theories.class)
public static class Pour les chaînes{
    }

    @RunWith(Theories.class)
public static class Pour les nombres et les chaînes{
    }

    @RunWith(Theories.class)
public static class Pour les nombres et les nombres{
    }
}

2. Annotation théorique

Pour les méthodes de test qui souhaitent utiliser des paramètres, Ajoutez une annotation Théorie au lieu de l'annotation Test.

Les méthodes de test avec l'annotation Theory peuvent déclarer des arguments arbitraires. Membres avec les annotations DataPoint et DataPoints décrites ci-dessous Défini dans l'argument en tant que paramètre.

@RunWith(Enclosed.class)
public class ParameterizedTest {

    @RunWith(Theories.class)
public static class Pour les nombres{
        @Theory
        public void testCase(int num) throws Exception {
        }
    }

    @RunWith(Theories.class)
public static class Pour les chaînes{
        @Theory
        public void testCase(String str) throws Exception {
        }
    }

    @RunWith(Theories.class)
public static class Pour les nombres et les chaînes{
        @Theory
        public void testCase(int num, String str) throws Exception {
        }
    }

    @RunWith(Theories.class)
public static class Pour les nombres et les nombres{
        @Theory
        public void testCase(int num1, int num2) throws Exception {
        }
    }
}

3. 3. Préparation des paramètres

Annotation DataPoint

Dans le test paramétré à l'aide du lanceur de test Theories, Définissez les paramètres à l'aide de l'annotation DataPoint. Les paramètres sont définis dans des champs ou méthodes statiques et publics.

L'argument de la méthode avec l'annotation Theory est Parmi les valeurs avec l'annotation DataPoint, les paramètres qui correspondent au type Toutes les combinaisons sont passées en arguments.

@RunWith(Enclosed.class)
public class ParameterizedTest {

    @RunWith(Theories.class)
public static class Pour les nombres{
        @DataPoint
        public static int INT_PARAM_1 = 3;
        @DataPoint
        public static int INT_PARAM_2 = 4;
        
        @Theory
        public void testCase(int num) throws Exception {
        	System.out.println("Valeur d'entrée:" + num);
        }
    }

    @RunWith(Theories.class)
public static class Pour les chaînes{
        @DataPoint
        public static String STR_PARAM_1 = "Hello";
        @DataPoint
        public static String STR_PARAM_2 = "World";
        
        @Theory
        public void testCase(String str) throws Exception {
        	System.out.println("Valeur d'entrée:" + str);
        }
    }

    @RunWith(Theories.class)
public static class Pour les nombres et les chaînes{
    	@DataPoint
        public static int INT_PARAM_1 = 3;
        @DataPoint
        public static int INT_PARAM_2 = 4;
        @DataPoint
        public static String STR_PARAM_1 = "Hello";
        @DataPoint
        public static String STR_PARAM_2 = "World";
        
        @Theory
        public void testCase(int num, String str) throws Exception {
        	System.out.println("Valeur d'entrée:" + num + "、" + str);
        }
    }

    @RunWith(Theories.class)
public static class Pour les nombres et les nombres{
    	@DataPoint
        public static int INT_PARAM_1 = 3;
        @DataPoint
        public static int INT_PARAM_2 = 4;
        
        @Theory
        public void testCase(int num1, int num2) throws Exception {
        	System.out.println("Valeur d'entrée:" + num1 + "、" + num2);
        }
    }
}

Lorsque ParameterizedTest est exécuté, le contenu suivant est envoyé à la console.

~ Dans le cas d'une valeur numérique ~
Valeur d'entrée: 3
Valeur d'entrée: 4

~ Dans le cas d'une chaîne de caractères ~
Valeur d'entrée: Bonjour
Valeur d'entrée: Monde

~ Pour les nombres et les chaînes ~
Valeur d'entrée: 3, bonjour
Valeur d'entrée: 3, monde
Valeur d'entrée: 4, bonjour
Valeur d'entrée: 4, monde

~ Dans le cas de valeurs numériques et de valeurs numériques ~
Valeur d'entrée: 3, 3
Valeurs d'entrée: 3, 4
Valeurs d'entrée: 4, 3
Valeurs d'entrée: 4, 4

Utilisation d'objets de fixation

Si plus d'arguments sont passés à la méthode Theory En déclarant un DTO statique dans la classe de test, les arguments peuvent être combinés en un seul. Le DTO qui contient l'argument est appelé un objet fixture.

@RunWith(Theories.class)
public class ParameterizedTest {
    @DataPoint
    public static Fixture INT_PARAM_1 = new Fixture(1, 2, 3);
    @DataPoint
    public static Fixture INT_PARAM_2 = new Fixture(0, 2, 2);

    @Theory
    public void testCase(Fixture params) throws Exception {
        assertThat(params.x + params.y, is(params.expected));
    }

    static class Fixture {
        int x;
        int y;
        int expected;

        Fixture(int x, int y, int expected) {
            this.x = x;
            this.y = y;
            this.expected = expected;
        }
    }
}

Annotation DataPoints

L'annotation DataPoint ne pouvait définir qu'un seul paramètre, Avec l'annotation DataPoints, plusieurs paramètres peuvent être définis en un seul endroit.

@RunWith(Theories.class)
public class ParameterizedTest {
    @DataPoints
    public static Fixture[] INT_PARAMS = {
        new Fixture(1, 2, 3),
        new Fixture(0, 2, 2),
    };
    /*Définition à l'aide de l'annotation DataPoint
    @DataPoint
    public static Fixture INT_PARAM_1 = new Fixture(1, 2, 3);
    @DataPoint
    public static Fixture INT_PARAM_2 = new Fixture(0, 2, 2);
    */

    @Theory
    public void testCase(Fixture params) throws Exception {
        assertThat(params.x + params.y, is(params.expected));
    }

    static class Fixture {
        int x;
        int y;
        int expected;

        Fixture(int x, int y, int expected) {
            this.x = x;
            this.y = y;
            this.expected = expected;
        }
    }
}

Ressource externe (fichier YAML)

params.yaml


!!seq [
  !!test.ParameterizedTest$Fixture
  { x: 1, y: 2, expected: 3 },
  !!test.ParameterizedTest$Fixture
  { x: 0, y: 2, expected: 2 },
]
@RunWith(Theories.class)
public class ParameterizedTest {
    @DataPoints
    public static Fixture[] INT_PARAMS = {
        InputStream in = ParameterizedTest.class
                            .getResourceAsStream("params.yaml");
        return ((List<Fixture>) new Yaml().load(in)).toArray(new Fixture[0]);
    };
    /*Définition à l'aide de l'annotation DataPoint
    @DataPoint
    public static Fixture INT_PARAM_1 = new Fixture(1, 2, 3);
    @DataPoint
    public static Fixture INT_PARAM_2 = new Fixture(0, 2, 2);
    */

    @Theory
    public void testCase(Fixture params) throws Exception {
        assertThat(params.x + params.y, is(params.expected));
    }

    static class Fixture {
        int x;
        int y;
        int expected;

        Fixture(int x, int y, int expected) {
            this.x = x;
            this.y = y;
            this.expected = expected;
        }
    }
}

Quatre. Assumer la classe

Si vous spécifiez simplement les paramètres, toutes les combinaisons de tests seront exécutées. Dans un tel cas, certaines combinaisons peuvent ne pas correspondre aux valeurs attendues du test.

Par exemple, le test ci-dessus a vérifié si la valeur ajoutée était la même que la valeur attendue, S'il s'agit d'un test pour vérifier que la valeur ajoutée est égale Dans le cas de x = 1 et y = 2, la valeur d'entrée n'est pas celle attendue.

Dans ces cas, utilisez la classe Assume Seules les valeurs d'entrée prises en compte par le scénario de test peuvent être transmises.

Les méthodes fournies par la classe Assume incluent asumeTrue et assumeThat. Mettez en œuvre chacun comme suit.

assumeTrue(Expression conditionnelle);
assumeThat(Valeur d'entrée,méthode de mise en correspondance);

Quant au mouvement, lorsque le jugement de la méthode est faux dans les deux cas, En d'autres termes, si la valeur d'entrée n'est pas celle attendue, une AssumptionViolatedException sera générée. Aucun autre traitement n'est effectué. (Agit comme une clause de retour dans le code de test) Cette exception est traitée spécialement dans le lanceur de test, et même si l'exception est interceptée, le résultat du test réussira.

@RunWith(Theories.class)
public class ParameterizedTest {
    @DataPoints
    public static Fixture[] INT_PARAMS = {
        new Fixture(1, 2, 3),
        new Fixture(0, 2, 2),
    };

    @Theory
    public void testCase(Fixture params) throws Exception {
        //La valeur d'entrée est supposée être une valeur paire après l'addition.
        //Valeur d'entrée inattendue(assumeTrue(false)Que devient)Dans le cas de, le traitement ultérieur n'est pas effectué
        assumeTrue((params.x + params.y) % 2 == 0);
        // x + y =Vérifiez qu'il est pair
        assertThat((params.x + params.y) % 2 == 0 , is(true));
    }

    static class Fixture {
        int x;
        int y;
        int expected;

        Fixture(int x, int y, int expected) {
            this.x = x;
            this.y = y;
            this.expected = expected;
        }
    }
}

Problèmes de test paramétrés

Couverture des données

L'exhaustivité, qui est un indice de qualité du test, dépend de la spécification des paramètres et de la classe Assume. Puisque c'est l'implémenteur qui fait le filtrage, juste parce que toutes les combinaisons sont testées Ce n'est pas un cas de test qui couvre les exigences et les spécifications. Par conséquent, en ce qui concerne la validité et l'exhaustivité des données de test, sous la direction de l'examinateur qui connaît la technique de test Il est nécessaire de prendre des mesures telles que la vérification.

Manque d'informations sur les paramètres

Lors du test avec Theories test runner, le rapport s'affiche lorsque le test échoue L'information «Quel paramètre a échoué?» Est manquante. Par conséquent, dans le test paramétré, il est nécessaire de prendre des mesures en tenant compte de la facilité d'investigation lorsque le test échoue.

/*Exemple) Utilisez la méthode assertThat pour envoyer la valeur d'entrée au message au moment de l'échec*/
@Theory
public void testCase(Fixture params) throws Exception {
    assumeTrue((params.x + params.y) % 2 == 0);
    String failMsg = "Fail when x = " + params.x + ", y = " + params.y;
    assertThat(failMsg, (params.x + params.y) % 2 == 0 , is(true));
}

Les références

Cet article a été rédigé en référence aux informations suivantes.

Recommended Posts

Introduction au test de paramétrage dans JUnit
Premiers pas avec DBUnit
Premiers pas avec Ruby
Premiers pas avec Swift
Premiers pas avec Doma-Transactions
Premiers pas avec le traitement Doma-Annotation
Premiers pas avec Java Collection
Premiers pas avec JSP et servlet
Premiers pas avec les bases de Java
Premiers pas avec Spring Boot
Premiers pas avec les modules Ruby
Test de la logique d'envoi et de réception de demande avec MockWebServer dans JUnit
Premiers pas avec Java_Chapitre 5_Exercices pratiques 5_4
[Google Cloud] Premiers pas avec Docker
Premiers pas avec Docker avec VS Code
Premiers pas avec Doma-Criteria API Cheet Sheet
Premiers pas avec Ruby pour les ingénieurs Java
Introduction à Java à partir de 0 Partie 1
Premiers pas avec Ratpack (4) - Routage et contenu statique
Démarrez avec le fonctionnement de JVM GC
Premiers pas avec Language Server Protocol avec LSP4J
Premiers pas avec la création d'ensembles de ressources avec ListResoueceBundle
Liens et mémos pour démarrer avec Java (pour moi-même)
Premiers pas avec Doma-Using Projection avec l'API Criteira
Premiers pas avec les sous-requêtes utilisant Doma avec l'API Criteria
Premiers pas avec Java 1 Assembler des éléments similaires
Premiers pas avec Kotlin à envoyer aux développeurs Java
Premiers pas avec Doma-Using Joins avec l'API Criteira
Test avec com.google.testing.compile
Refactoring dans JUnit
Premiers pas avec Doma - Introduction à l'API Criteria
J'ai essayé de démarrer avec Gradle sur Heroku
Prise en main des programmes Java à l'aide de Visual Studio Code
[JUnit 5] Ecrivez un test de validation avec Spring Boot! [Test de paramétrage]
Premiers pas avec les anciens ingénieurs Java (Stream + Lambda)
Faites un test unitaire avec Junit.
Commencez avec Gradle
Premiers pas avec Java et création d'un éditeur Ascii Doc avec JavaFX
Premiers pas avec Doma-Dynamic Construction de clauses WHERE avec l'API Criteria
Premiers pas avec Reactive Streams et l'API JDK 9 Flow
Points à garder à l'esprit lors du test de méthodes privées dans JUnit
Premiers pas avec Git Hub Container Registry au lieu de Docker Hub
Exemple de code pour la simulation de base et les tests avec Mockito 3 + JUnit 5