Faire des tests combinatoires avec les theories JUnit (partie2)

Lors du précédent post je vous parlais des Theories. Aujourd'hui je vous propose d'aller un peu plus loin dans la définition du jeu de données en faisant de la combinatoire et en définissant un jeu de données dynamiquement.

Disons que l'on veuille tester une méthode avec un jeu de données spécifié sous forme de tableau. Nous allons utiliser l'annotation @ParametersSuppliedBy :

Définissons une annotation IntegerArrayParameters où l'on pourra spécifier une liste de int :

package com.gisgraphy.samples;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import org.junit.experimental.theories.ParametersSuppliedBy;

@Retention(RetentionPolicy.RUNTIME)
@ParametersSuppliedBy(IntegerArayParametersSupplier.class)
public @interface IntegerArrayParameters {
    int[] values();
}

Il nous faut aussi définir une autre annotation qui créera le jeu de données à partir des valeurs rentrées, (cela peut paraître un peu obscure et redondant mais cela deviendra sans doute plus clair avec le deuxième exemple) :

package com.gisgraphy.samples;

import java.util.ArrayList;
import java.util.List;

import org.junit.experimental.theories.ParameterSignature;
import org.junit.experimental.theories.ParameterSupplier;
import org.junit.experimental.theories.PotentialParameterValue;


public class IntegerArayParametersSupplier extends ParameterSupplier {
    @Override
    public List<PotentialParameterValue> getValueSources(Object test, ParameterSignature sig) {
        IntegerArrayParameters annotation = (IntegerArrayParameters) sig.getSupplierAnnotation();
        List<PotentialParameterValue> list = new ArrayList<PotentialParameterValue>();
        for (int value : annotation.values()) {
            list.add(PotentialParameterValue.forValue(value));
        }
        return list;
    }
}

On peut alors définir un jeu de données de int et faire de la combinatoire :

package com.gisgraphy.samples;

import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;

@RunWith(Theories.class)
public class AbstractHomeControllerTest {
    @Theory
    public void testHandleRequestInternal(@IntegerArrayParameters(values = {1,2,3}) int param1,
            @IntegerArrayParameters(values = {4,5,6}) int param2)  throws Exception {
        System.out.println("param1=" + param1 + " param2=" + param2);

        //do the stuff
        
    }
}

Afin de montrer la combinatoire, voici la sortie de la console lorsqu'on joue le test :


param1=1 param2=4
param1=1 param2=5
param1=1 param2=6
param1=2 param2=4
param1=2 param2=5
param1=2 param2=6
param1=3 param2=4
param1=3 param2=5
param1=3 param2=6

Deuxième exemple : Reprenons la classe de la partie 1 sur les théories, avec la classe qui calculait la racine carrée. Nous avions un assumeTrue pour différencier les valeurs positives des valeurs négatives qui n'était pas très élégant du tout. Procédons différemment et définissons un jeu de données qui balaye les nombres compris entre deux valeurs spécifiées :

Définissons notre RangeParameter :

package com.gisgraphy.samples;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import org.junit.experimental.theories.ParametersSuppliedBy;

@Retention(RetentionPolicy.RUNTIME)
@ParametersSuppliedBy(RangeParametersSupplier.class)
public @interface RangeParameters {
    int from();
    int to();
}

Puis un RangeParametersSupplier :

package com.gisgraphy.samples;

import java.util.ArrayList;
import java.util.List;

import org.junit.experimental.theories.ParameterSignature;
import org.junit.experimental.theories.ParameterSupplier;
import org.junit.experimental.theories.PotentialParameterValue;


public class RangeParametersSupplier extends ParameterSupplier {
    @Override
    public List<PotentialParameterValue> getValueSources(Object test, ParameterSignature sig) {
        RangeParameters annotation = (RangeParameters) sig.getSupplierAnnotation();
        List<PotentialParameterValue> list = new ArrayList<PotentialParameterValue>();
        for (int i=annotation.from(); i<=annotation.to(); i++) {
            list.add(PotentialParameterValue.forValue(i));
        }
        return list;
    }
}

Enfin reprenons la classe initiale et modifions la pour tester toutes les valeurs de 1 à 10 :

import junit.framework.TestCase;

import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;

@RunWith(Theories.class)
public class SqrtTest extends TestCase {
    public static int count = 0;


//La classe à tester    
public static class MySqrt {
        public static Double GetSqrt(int param) {
            if (param < 0){
                throw new IllegalArgumentException(
                           "Param should not be negative");
            }
            return Math.sqrt(param);
        }
    }


    @Theory
    public void testSqrtShouldReturnTheCorrectValue(
                               @RangeParameters(from=1,to=10) int value) {
        System.out.println("value="+value);
        assertEquals(Math.sqrt(value), MySqrt.GetSqrt(value));
    }
   
}

Pour preuve, voici la sortie standard :

value=1
value=2
value=3
value=4
value=5
value=6
value=7
value=8
value=9
value=10

On peut alors définir des jeux de données très facilement.

La discussion continue ailleurs

URL de rétrolien : https://davidmasclet.gisgraphy.com/index.php?trackback/6

Fil des commentaires de ce billet