web-dev-qa-db-fra.com

Créer plusieurs jeux de paramètres dans une classe paramétrée (junit)

Actuellement, je dois créer une classe de test paramétrée pour chaque méthode que je veux tester avec plusieurs entrées différentes. Existe-t-il un moyen d'ajouter cela ensemble dans un seul fichier?

À l'heure actuelle, il y a CalculatorTestAdd.Java Qui a un ensemble de paramètres qui sont utilisés pour vérifier si la fonction Add() fonctionne correctement. Existe-t-il une possibilité pour moi de "connecter" cet ensemble à la fonction Add() et de créer un ensemble supplémentaire destiné à la méthode Subtract() et d'ajouter cette méthode dans la même classe de test, résultant en un fichier appelé CalculatorTest.Java?

46
Jeroen Vannevel

Oui. Il n'y a rien de spécial à faire. Pour chaque ensemble de valeur (s) des paramètres, chaque méthode @Test est exécutée une fois, il suffit donc d'avoir une méthode test add () et une autre méthode test subtract ().

Puis-je également ajouter que la personne qui dicte cette exigence est erronée. Il ne sert à rien de dicter certains modèles de conception "pour tous les cas" - autant engager des singes entraînés.

8
Bohemian

Cette réponse est similaire à celle de Tarek (la partie paramétrée), bien que je pense qu'elle soit un peu plus extensible. Résout également votre problème et vous n'aurez pas échoué aux tests si tout est correct:

@RunWith(Parameterized.class)
public class CalculatorTest {
    enum Type {SUBSTRACT, ADD};
    @Parameters
    public static Collection<Object[]> data(){
        return Arrays.asList(new Object[][] {
          {Type.SUBSTRACT, 3.0, 2.0, 1.0},
          {Type.ADD, 23.0, 5.0, 28.0}
        });
    }

    private Type type;
    private Double a, b, expected;

    public CalculatorTest(Type type, Double a, Double b, Double expected){
        this.type = type;
        this.a=a; this.b=b; this.expected=expected;
    }

    @Test
    public void testAdd(){
        Assume.assumeTrue(type == Type.ADD);
        assertEquals(expected, Calculator.add(a, b));
    }

    @Test
    public void testSubstract(){
        Assume.assumeTrue(type == Type.SUBSTRACT);
        assertEquals(expected, Calculator.substract(a, b));
    }
}
52
nessa.gp

Je suis sûr que vous n'avez plus ce problème, mais j'ai pensé à 3 façons de le faire, chacune avec ses avantages et ses inconvénients. Avec le coureur paramétré, vous devrez utiliser une solution de contournement.

- Utilisation de plus de paramètres avec paramétré

Dans le cas où vous devez charger les paramètres en externe, vous ajoutez simplement un paramètre pour les résultats attendus.

Avantages : moins de codage, et il exécute tous les tests.

Inconvénients : nouveaux paramètres pour chaque ensemble différent de tests.

@RunWith(Parameterized.class)
public class CalculatorTest extends TestCase {
    private Calculator calculator;
    private int operator1;
    private int operator2;
    private int expectedSum;
    private int expectedSub;

    public CalculatorTest(int operator1, int operator2, int expectedSum, int expectedSub) {
        this.operator1 = operator1;
        this.operator2 = operator2;
    }

    @Params
    public static Collection<Object[]> setParameters() {
        Collection<Object[]> params = new ArrayList<>();
        // load the external params here
        // this is an example
        params.add(new Object[] {2, 1, 3, 1});
        params.add(new Object[] {5, 2, 7, 3});

        return params;
    }

    @Before
    public void createCalculator() {
        calculator = new Calculator();
    }

    @Test
    public void addShouldAddTwoNumbers() {
        assertEquals(expectedSum, calculator.add(operator1, operator2));
    }

    @Test
    public void subtractShouldSubtractTwoNumbers() {
        assertEquals(expectedSub, calculator.subtract(operator1, operator2));
    }

    @After
    public void endTest() {
        calculator = null;
        operator1 = null;
        operator2 = null;
        expectedSum = null;
        expectedSub = null;
    }
}

- Ne pas utiliser de coureur paramétré

Cela fonctionne bien si vous définissez vos paramètres par programmation.

Avantages : Vous pouvez avoir autant de tests que vous le souhaitez sans avoir à définir un énorme ensemble de paramètres.

Inconvénients : Plus de codage, et il s'arrête au premier échec (ce qui n'est peut-être pas un con).

@RunWith(JUnit4.class)
public class CalculatorTest extends TestCase {
    private Calculator calculator;

    @Before
    public void createCalculator() {
        calculator = new Calculator();
    }

    @Test
    public void addShouldAddTwoNumbers() {
        int[] operator1 = {1, 3, 5};
        int[] operator2 = {2, 7, 9};
        int[] expectedResults = {3, 10, 14};

        for (int i = 0; i < operator1.length; i++) {
            int actualResult = calculator.add(operator1[i], operator2[i]);
            assertEquals(expectedResults[i], actualResult);
        }
    }

    @Test
    public void subtractShouldSubtractTwoNumbers() {
        int[] operator1 = {5, 8, 7};
        int[] operator2 = {1, 2, 10};
        int[] expectedResults = {4, 6, -3};

        for (int i = 0; i < operator1.length; i++) {
            int actualResult = calculator.subtract(operator1[i], operator2[i]);
            assertEquals(expectedResults[i], actualResult);
        }
    }

    @After
    public void endTest() {
        calculator = null;
    }
}

- Utilisation de JUnitParams

Je n'ai aucune affiliation avec les pragmatiques, je viens de le découvrir il y a quelques jours. Ce framework s'exécute au-dessus de JUnit et gère les tests paramétrés différemment. Les paramètres sont transmis directement à la méthode de test, vous pouvez donc avoir dans la même classe différents paramètres pour différentes méthodes.

Avantages : obtient les mêmes résultats que les solutions ci-dessus sans solution de contournement.

Inconvénients : peut-être que votre entreprise ne vous permet pas d'ajouter une nouvelle dépendance au projet ou vous oblige à utiliser une règle de codage bizarre (comme l'utilisation des coureurs paramétrés exclusivement). Avouons-le, cela arrive plus que nous ne le souhaiterions.

Ici est un bel exemple de JUnitParams en action, et vous pouvez obtenir le projet/vérifier le code sur cette page Github .

13
Tarek

vous pouvez utiliser des paramètres avec https://github.com/piotrturski/zohhak :

@TestWith({
   "1, 7, 8",
   "2, 9, 11"
})
public void addTest(int number1, int number2, int expectedResult) {
    BigDecimal result = calculator.add(number1, number2);
    assertThat(result).isEqualTo...
}

si vous souhaitez charger des paramètres à partir d'un fichier, vous pouvez utiliser http://code.google.com/p/fuzztester/ ou http://code.google.com/p/ junitparams /

et si vous avez besoin d'une réelle flexibilité, vous pouvez utiliser @Parameterized de junit mais cela encombre votre code. vous pouvez également utiliser les théories de junit - mais il semble exagéré pour les tests de calculatrice

10
piotrek

Une autre solution JUnit pure mais élégante à mon avis consiste à encapsuler chaque test paramétré (s) dans leur propre classe statique interne et à utiliser le lanceur de test Enclosed sur la classe de test de niveau supérieur. Cela vous permet non seulement d'utiliser des valeurs de paramètres différentes pour chaque test indépendamment les uns des autres, mais également de tester des méthodes avec des paramètres complètement différents.

Voici à quoi cela ressemblerait:

@RunWith(Enclosed.class)
public class CalculatorTest {

  @RunWith(Parameterized.class)
  public static class AddTest {

    @Parameters
    public static Collection<Object[]> data() {
      return Arrays.asList(new Object[][] {
          { 23.0, 5.0, 28.0 }
      });
    }

    private Double a, b, expected;

    public AddTest(Double a, Double b, Double expected) {
      this.a = a;
      this.b = b;
      this.expected = expected;
    }

    @Test
    public void testAdd() {
      assertEquals(expected, Calculator.add(a, b));
    }
  }

  @RunWith(Parameterized.class)
  public static class SubstractTest {

    @Parameters
    public static Collection<Object[]> data() {
      return Arrays.asList(new Object[][] {
          { 3.0, 2.0, 1.0 }
      });
    }

    @Parameter(0)
    private Double a;
    @Parameter(1)
    private Double b;
    @Parameter(2)
    private Double expected;

    @Test
    public void testSubstract() {
      assertEquals(expected, Calculator.substract(a, b));
    }
  }

  @RunWith(Parameterized.class)
  public static class MethodWithOtherParametersTest {

    @Parameters
    public static Collection<Object[]> data() {
      return Arrays.asList(new Object[][] {
          { 3.0, 2.0, "OTHER", 1.0 }
      });
    }

    private Double a;
    private BigDecimal b;
    private String other;
    private Double expected;

    public MethodWithOtherParametersTest(Double a, BigDecimal b, String other, Double expected) {
      this.a = a;
      this.b = b;
      this.other = other;
      this.expected = expected;
    }

    @Test
    public void testMethodWithOtherParametersTest() {
      assertEquals(expected, Calculator.methodWithOtherParametersTest(a, b, other));
    }
  }

  public static class OtherNonParameterizedTests {

    // here you can add any other test which is not parameterized

    @Test
    public void otherTest() {
      // test something else
    }
  }
}

Notez l'utilisation du @Parameter annotation dans le SubstractTest, que je considère plus lisible. Mais c'est plus une question de goût.

10
Alex

Eh bien, maintenant JUnit-5 vous offre une solution à cela - en redéfinissant une façon d'écrire des tests paramétrés. Désormais, un test paramétré peut être défini au niveau d'une méthode en utilisant @ ParameterizedTest et peut recevoir une source de méthode en utilisant @ MethodSource.

Donc, dans votre cas, vous pouvez avoir 2 méthodes de source de données distinctes pour fournir des données d'entrée pour vos méthodes de test add () et subtract (), toutes deux dans la même classe. Votre code devrait ressembler à ceci:

public class CalculatorTest{
    public static int[][] dataSetForAdd() {
        return new int[][] { { 1 , 2, 3 }, { 2, 4, 6 }, { 121, 4, 125 } };
    }

    public static int[][] dataSetForSubtract() {
        return new int[][] { { 1 , 2, -1 }, { 2, 4, -2 }, { 121, 4, 117 } };
    }

    @ParameterizedTest
    @MethodSource(names = "dataSetForAdd")
    void testCalculatorAddMethod(int[] dataSetForAdd) {
        Calculator calculator= new Calculator();
        int m1 = dataSetForAdd[0];
        int m2 = dataSetForAdd[1];
        int expected = dataSetForAdd[2];
        assertEquals(expected, calculator.add(m1, m2));
    }

    @ParameterizedTest
    @MethodSource(names = "dataSetForSubtract")
    void testCalculatorAddMethod(int[] dataSetForSubtract) {
        Calculator calculator= new Calculator();
        int m1 = dataSetForSubtract[0];
        int m2 = dataSetForSubtract[1];
        int expected = dataSetForSubtract[2];
        assertEquals(expected, calculator.subtract(m1, m2));
    }
}
5
Ankit Singodia

Avec Junit Jupiter: https://www.petrikainulainen.net/programming/testing/junit-5-tutorial-writing-parameterized-tests/

import intf.ICalculator;

public class Calculator implements ICalculator {
    @Override
    public int plus(int a, int b) {return a + b; }

    @Override
    public int minuis(int a, int b) {return a - b;}

    @Override
    public int multy(int a, int b) {return a * b;}

    @Override  // check in junit byZero
    public int divide(int a, int b) {return a / b;}

}

Classe de test:

import static org.junit.Assert.assertEquals;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

class CalculatorJupiter5Test {

    Calculator calculator = new Calculator();

    @DisplayName("Should calculate the correct sum")
    @ParameterizedTest(name = "{index} => a={0}, b={1}, sum={2}")
    @CsvSource({
            "5, 3, 8",
            "1, 3, 4",
            "6, 6, 12",
            "2, 3, 5"
    })
    void sum(int a, int b, int sum) {
        assertEquals(sum, calculator.plus(a, b) );
    }

    @DisplayName("Should calculate the correct multy")
    @ParameterizedTest(name = "{index} => a={0}, b={1}, multy={2}")
    @CsvSource({
        "5, 3, 15",
        "1, 3, 3",
        "6, 6, 36",
        "2, 3, 6"
    })
    void multy(int a, int b, int multy) {
        assertEquals(multy, calculator.multy(a, b) );
    }

    @DisplayName("Should calculate the correct divide")
    @ParameterizedTest(name = "{index} => a={0}, b={1}, divide={2}")
    @CsvSource({
        "5, 3, 1",
        "14, 3, 4",
        "6, 6, 1",
        "36, 2,  18"
    })
    void divide(int a, int b, int divide) {
        assertEquals(divide, calculator.divide(a, b) );
    }

   @DisplayName("Should calculate the correct divide by zero")
   @ParameterizedTest(name = "{index} => a={0}, b={1}, divide={2}")
   @CsvSource({
      "5, 0, 0",
   })
    void divideByZero(int a, int b, int divide) {
     assertThrows(ArithmeticException.class,
         () -> calculator.divide(a , b),
         () -> "divide by zero");
    }

    @DisplayName("Should calculate the correct minuis")
    @ParameterizedTest(name = "{index} => a={0}, b={1}, minuis={2}")
    @CsvSource({
        "5, 3, 2",
        "1, 3, -2",
        "6, 6, 0",
        "2, 3, -1"
    })
    void minuis(int a, int b, int minuis) {
        assertEquals(minuis, calculator.minuis(a, b) );
    }
}
2
Fox user9219598

J'utilise junitparams, ce qui me permet de passer un ensemble distinct de paramètres dans chaque test. JunitParams utilise des méthodes pour renvoyer un ensemble de paramètres, et dans le test, vous fournissez les noms de méthode comme source d'entrée de paramètre, donc changer le nom de la méthode changera l'ensemble de données.

import com.xx.xx.xx.Transaction;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import Java.util.Arrays;
import Java.util.List;
import Java.util.Set;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;


@RunWith(JUnitParamsRunner.class)
public class IpAddressValidatorTest {

    private Validator validator;

    @Before
    public void setUp() {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        validator = factory.getValidator();

    }

    public static List<String> goodData() {
        return Arrays.asList(
                "10.10.10.10",
                "127.0.0.1",
                "10.136.182.1",
                "192.168.1.1",
                "192.168.1.1",
                "1.1.1.1",
                "0.0.0.0"
        );
    }

    public static List<String> badData() {
        return Arrays.asList(
                "01.01.01.01",
                "255.255.255.256",
                "127.1",
                "192.168.0.0"
        );
    }

    @Test
    @Parameters(method = "goodData")
    public void ipAddressShouldBeValidated_AndIsValid(String ipAddress) {
        Transaction transaction = new Transaction();
        transaction.setIpAddress(ipAddress);
        Set<ConstraintViolation<Transaction>> violations = validator.validateProperty(transaction, "ipAddress");
        assertTrue(violations.isEmpty());
    }

    @Test
    @Parameters(method = "badData")
    public void ipAddressShouldBeValidated_AndIsNotValid(String ipAddress) {
        Transaction transaction = new Transaction();
        transaction.setIpAddress(ipAddress);
        Set<ConstraintViolation<Transaction>> violations = validator.validateProperty(transaction, "ipAddress");
        assertFalse(violations.isEmpty());
    }


}
0
WesternGun