Create multiple parameter sets in one parameterize

2019-03-08 23:25发布

Currently I have to create a parameterized test class for every method that I want to test with several different inputs. Is there a way to add this together in one file?

Right now there's CalculatorTestAdd.java which has a set of parameters that are used to check if the Add() function works properly. Is there a possbility for me to 'connect' this set to the Add() function and create an additional set meant for the Subtract() method and add this method in the same test class, resulting in one file called CalculatorTest.java?

7条回答
甜甜的少女心
2楼-- · 2019-03-08 23:55

Yes. There's nothing special you have to do. For every set of value(s) of the parameters, each @Test method is run once, so just have one method test add() and another method test subtract().

May I also add that the person who is dictating this requirement is misguided. There is little value in dictating certain design patterns "for all cases" - might as well hire trained monkeys.

查看更多
别忘想泡老子
3楼-- · 2019-03-08 23:57

Another pure JUnit but yet elegant solution in my view is to encapsulate each parameterized test(s) in their own inner static class and use the Enclosed test runner on the top level test class. This allows you not only to use different parameter values for each test independently of each other but also to test methods with completely different parameters.

This is how it would look like:

@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
    }
  }
}

Note the usage of the @Parameter annotation in the SubstractTest, which I consider more readable. But this is more a matter of taste.

查看更多
贪生不怕死
4楼-- · 2019-03-08 23:58

Well, now JUnit-5 offers you a solution for this - by redefining a way to write parameterized tests. Now a parameterized test can be defined at a method level using @ParameterizedTest and can be given a method source using @MethodSource.

So in your case you can have 2 separate data source methods for providing input data for your add() and subtract() test methods, both in the same class. Your code should go something like this:

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楼-- · 2019-03-09 00:00

This answer is similar to Tarek's one (the parametrized part), although I think it is a bit more extensible. Also solves your problem and you won't have failed tests if everything is 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));
    }
}
查看更多
戒情不戒烟
6楼-- · 2019-03-09 00:00

you can use parameters with 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...
}

if you want to load parameters from file, you can use http://code.google.com/p/fuzztester/ or http://code.google.com/p/junitparams/

and if you need real flexibility you can use junit's @Parameterized but it clutters your code. you can also use junit's Theories - but it seems an overkill for calculator tests

查看更多
孤傲高冷的网名
7楼-- · 2019-03-09 00:02
 Junit Jupiter     
     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;}

    }

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

    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) );
        }
    }
查看更多
登录 后发表回答