web-dev-qa-db-fra.com

Paramétrage avec tableau dans Junit 5 (ou autre bibliothèque de test Java) de manière plus intelligente

J'essaie de paramétrer ce test:

@Test
public void reverseQuote(double[] qsp) throws Exception {
...}

Il me semble absurde qu’il n’existe pas de méthode rapide pour initialiser un tableau qsp comme, par exemple, ValueSource:

@ParameterizedTest
@ValueSource(ints = { 1, 2, 3 })
void testWithValueSource(int argument) {
    assertNotNull(argument);
}

mon but est de faire quelque chose comme @ValueSource(doublesArray = {new double[]{1.0, 2.0, 3.0}}) (qui retourne maintenant une erreur). N'existe-t-il rien qui permette quelque chose de similaire ?? Autres réponses ne semblent suggérer que des méthodes élaborées, comme utiliser @MethodSource ou @ConvertWith

J'accepte aussi les réponses implémentant d'autres bibliothèques de test.

19
Lore

L'utilisation d'une combinaison de tests paramétrés pour Junit et d'analyse YAML peut être envisagée. 

@RunWith(Parameterized.class)
public class AnotherParameterizedTest {

    private final HashMap row;

    @Parameterized.Parameters(name="Reverse Lists Tests # {index}:")
    public static List<Map<String, Object>> data() {
        final TestData testData = new TestData(""+
             "|   ID   |       List         |  Expected   |                \n"+
             "|   0    |    [1, 2, 3]       |  [3, 2, 1]  |                \n"+
             "|   1    |    [2, 3, 5]       |  [3, 2, 1]  |                \n"+
             "|   2    |    [5, 6, 7]       |  [ 7, 6, 5] |                \n"
        );
        // parsing each row using simple YAML parser and create map per row
        return testData.getDataTable();
    }

    // Each row from the stringified table above will be 
    // split into key=value pairs where the value are parsed using a 
    // yaml parser. this way, values can be pretty much any yaml type
    // like a list of integers in this case. 
    public AnotherParameterizedTest(HashMap obj) {
        this.row = obj;
    }

    @Test
    public void test() throws Exception {
        List orgListReversed = new ArrayList((List) row.get("List"));
        Collections.reverse(orgListReversed);
        assertEquals((List) row.get("Expected"), orgListReversed);
    }

}

Au lieu d'utiliser une chaîne, j'utilise Excel Reader pour faire la même chose avec de simples Tableaux Excel. Analyser chaque ligne dans une carte en utilisant YAML pour les valeurs.

Junit IDE Résultats du test

La même chose que nous venons de tester avec Junit Jupiter donne de meilleurs résultats dans le IDE Runner. 

import static org.junit.jupiter.api.Assertions.assertEquals;

import de.deicon.yatf.runner.dsl.TestData;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

import Java.util.Collections;
import Java.util.List;
import Java.util.Map;

public class FirstTest {

    @ParameterizedTest
    @MethodSource("testTable")
    public void test(Map row){
        List reversedList = (List) row.get("List");
        Collections.reverse(reversedList);
        assertEquals((List)row.get("Expected"), reversedList);
    }

    static List<Map<String, Object>> testTable() {
        return new TestData(""+
                "|ID|   List                  |Expected               |         \n"+
                "|0 | [1,2,3]                 | [3,2,1]               |         \n"+
                "|1 | [hans, peter, klaus]    | [klaus, peter, hans]  |         \n"
        ).getDataTable();
    }

}

 enter image description here

7
Dieter

Ok, ça va être une réponse bizarre, mais ça marche et c'était plutôt amusant à faire.

Première chose: votre chemin est impossible. Pas à cause de JUnit ni d'aucune API associée, mais à cause de Java - éléments de type d'annotation valides (les arguments d'annotation ne peuvent être que primitifs, String, Class, Enum, d'autres annotations et un tableau de tous ceux).

Deuxième chose: on peut contourner le premier. Vérifie ça:

@ArraySources(
  arrays = {
    @ArraySource(array = {1, 2, 3}),
    @ArraySource(array = {4, 5, 6}),
    @ArraySource(array = {7, 8, 9})
  }
)

Comme il est dit, l'annotation peut avoir d'autres annotations comme arguments, et des tableaux de ceux-ci, nous utilisons donc ces 2 règles ici.

Troisième chose: comment ça aide? Nous pouvons ajouter notre propre fournisseur annotation + argument, JUnit 5 est extensible de cette façon.

Les deux annotations:

@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ArgumentsSource(ArrayArgumentsProvider.class)
public @interface ArraySources {
    ArraySource[] arrays();
}

@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ArraySource {
    int[] array() default {};
}

Fournisseur d'arguments basé sur les annotations:

public class ArrayArgumentsProvider implements ArgumentsProvider, AnnotationConsumer<ArraySources> {
    private List<int[]> arguments;

    public void accept(ArraySources source) {
        List<ArraySource> arrays = Arrays.asList(source.arrays());

        this.arguments = arrays.stream().map(ArraySource::array).collect(Collectors.toList());
    }

    public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
        return this.arguments.stream().map(Arguments::of);
    }
}

Et le test final utilisant ceux-ci:

public class ArraySourcesTest {
    @ParameterizedTest
    @ArraySources(
            arrays = {
                    @ArraySource(array = {1, 2, 3}),
                    @ArraySource(array = {4, 5, 6}),
                    @ArraySource(array = {7, 8, 9})
            }
    )
    void example(int[] array) {
        System.out.println(Arrays.toString(array));
        System.out.println("Test Over");
    }
}

/* Output
[1, 2, 3]
Test Over
[4, 5, 6]
Test Over
[7, 8, 9]
Test Over
*/

Vous avez mentionné @MethodSource comme étant compliqué, eh bien, alors je pense que j'ai échoué dans cette affaire, mais cela fonctionne. Cela pourrait évidemment être simplifié et amélioré (comme nommer les arguments d'annotation comme valeurs par défaut - valeur - et je ne l'ai fait que pour int pour montrer l'idée). Vous n'êtes pas sûr de pouvoir obtenir le même résultat avec les fonctionnalités existantes (ArgumentsProvider et ArgumentSources), mais cela semble plus spécifique (vous savez que vous travaillez avec des tableaux) et montre les possibilités d'étendre JUnit5, peut être utile dans d'autres cas.

5
Shadov

J'aime utiliser Spock pour tester le code Java. C'est un framework de test basé sur groovy qui repose sur JUnit 4. Les tests paramétrés dans Spock sont une fonctionnalité intégrée:

def "The reverseQuote method doesn't return null"(double[] qsp) {

    when: "reverseQuote is called"
    double[] rev = reverseQuote(qsp)

    then: "the result is not null"
    null != rev

    where: "there are various input values"
    qsp << [
        [0.1, 0.2, 0.3] as double[],
        [1.0, 2.0, 3.0] as double[]
    ]
}

... vous pouvez également disposer vos données de test sous forme de tableau:

def "The reverseQuote method reverses the input array"(List qsp, List expected) {

    when: "reverseQuote is called"
    double[] rev = reverseQuote(qsp as double[])

    then: "the result is the reverse of the input"
    expected as double[] == rev

    where: "there are various input values"
    qsp             | expected
    [0.1, 0.2, 0.3] | [0.3, 0.2, 0.1]
    [1.0, 2.0, 3.0] | [3.0, 2.0, 1.0]
}

Remarque que la prévalence as double[] est une conséquence malheureuse de la conversion automatique des tableaux en listes, nous devons donc les réintégrer explicitement dans les cas particuliers dans lesquels nous interagissons avec du code Java nécessitant un tableau.

4
user31601

La documentation de JUnit suggère d’utiliser @MethodSource

@ParameterizedTest
@MethodSource("range")
void testWithRangeMethodSource(double argument) {
    assertNotEquals(9.0, argument);
}

static DoubleStream range() {
   return DoubleStream.range(0.0, 20.0);
}

Sinon, vous pourriez envisager d'utiliser ceci: https://junit.org/junit5/docs/5.0.2/api/org/junit/jupiter/params/provider/ValueSource.html#doubles

@ParameterizedTest
@ValueSource(doubles = { 1, 2, 3 })
public void test(double numberToTest){
   //do whatever
}
0
Ben Sch