Parameterized test?
Since JUnit4 we can now run the parameterized test, but what does that mean? It means that we can run one test multiple times with different values, great isn’t it?
How to?
Yeah that great, but how can I do that?
That simple :
Annotate your test class with @RunWith(Parameterized.class)
Create method annotated with @Parameters that returns an Iterable of Objects as test data set.
Create a constructor that takes as a parameter what is equivalent to one row of your dataset. Or create instance variable that will be equivalent as one parameter and annotate them with @Parameter(X) where X is the parameter index in a row of the dataset.
Create a test case that will use your instance variable.
Ok, but concretely can I have an example?
Exemple
Class to test
Let’s take a simple class, that take a String literal of a calcul as parameter and return the result.
package fr.eletutour; | |
public class Operation { | |
private int left; | |
private int right; | |
private String operation; | |
public Operation(String input){ | |
String[] in = input.split(" "); | |
this.left = Integer.parseInt(in[0]); | |
this.right = Integer.parseInt(in[2]); | |
this.operation = in[1]; | |
} | |
public int compute(){ | |
switch (operation){ | |
case "plus": | |
return left + right; | |
case "minus": | |
return left - right; | |
case "divided": | |
return left / right; | |
case "by": | |
return left * right; | |
default: | |
throw new IllegalArgumentException("Bad operator"); | |
} | |
} | |
} |
Classique Junit test
In a Classique way I wrote this test class to test each one of my case
package com.eletutour; | |
import fr.eletutour.Operation; | |
import org.junit.Assert; | |
import org.junit.Test; | |
public class OperationTest { | |
@Test | |
public void should_return_2_plus(){ | |
Operation op = new Operation("1 plus 1"); | |
Assert.assertEquals(2, op.compute()); | |
} | |
@Test | |
public void should_return_6_by(){ | |
Operation op = new Operation("2 by 3"); | |
Assert.assertEquals(6, op.compute()); | |
} | |
@Test | |
public void should_return_9_divided(){ | |
Operation op = new Operation("18 divided 2"); | |
Assert.assertEquals(9, op.compute()); | |
} | |
@Test | |
public void should_return_5_minus(){ | |
Operation op = new Operation("20 minus 15"); | |
Assert.assertEquals(5, op.compute()); | |
} | |
@Test(expected = IllegalArgumentException.class) | |
public void should_throw_exception(){ | |
Operation op = new Operation("20 exp 15"); | |
op.compute(); | |
} | |
} |
As you can see, I have one test per test case, here as my class is simple, it doesn’t have that much line, but what if my class was more complexe, it will be a lot more code, redundant isn’t it?
Parameterized
Here is the same test, but parameterized, you can see all the previous statement established in the How-to section
The runner
The dataset
The parameters
-
The test who use it
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characterspackage com.eletutour; import fr.eletutour.Operation; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; import java.util.Arrays; import static org.junit.Assert.assertEquals; @RunWith(Parameterized.class) public class OperationTestParameterized { @Parameter(0) public String testName; @Parameter(1) public String input; @Parameter(2) public int expectedNumber; @Parameter(3) public Class<? extends Exception> expectedException; @Parameter(4) public String expectedExceptionMsg; @Rule public ExpectedException thrown = ExpectedException.none(); @Parameters(name = "{0}") public static Iterable<Object[]> data() { return Arrays.asList(new Object[][] { // calculation scenarios: { "plus compute", "1 plus 1", 2, null, null }, { "minus compute", "8 minus 3", 5, null, null}, { "by compute", "3 by 4", 12, null, null}, { "divided compute", "8 divided 2", 4, null, null}, { "expected exception", "20 exp 15", 0, IllegalArgumentException.class, "Bad operator"} }); } @Test public void Operation_compute_test(){ Operation op = new Operation(input); if (expectedException != null) { thrown.expect(expectedException); thrown.expectMessage(expectedExceptionMsg); } assertEquals(expectedNumber, op.compute()); } }
Thanks for your reading time, the example in this article are in Junit4, if you want an exemple using Junit5, I will write another article about it.
Top comments (2)
Hi, here is how it is used at SystemDS, github.com/apache/systemds/blob/ma...
thanks, will read it later to improve how i do it.