JUnit : Parameterized tests and Suites

In the previous post we saw how to write Unit test. We can write test methods in different classes and run them.

Parameterized Tests

Carrying on with our previous project, we have MathUtils class whose isEven method we are going to test. MathUtils.java:


package com.mynotes.testing;

public class MathUtils {

public boolean isEven(String number) {
int x = Integer.parseInt(number);
if (x % 2 == 0) {
return true;
}
return false;
}
}

We often saw that many our test methods were exactly same except the input and expected output. This duplication of test code can solved by using parameterized test. We can pass a set of inputs and expected outputs into a test class and have our test run against those inputs and check with the expected results without having to create a test case for each pair.

Lets create a MathUtilsParameterizedTest.java


package com.mynotes.testing;

import static org.junit.Assert.assertEquals;

import java.util.Arrays;
import java.util.Collection;

import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

import org.junit.Test;

@RunWith(Parameterized.class)
public class MathUtilsParameterizedTest {


private String input;
private boolean expectedOutput;

public MathUtilsParameterizedTest(String input, boolean expectedOutput) {
this.input = input;
this.expectedOutput = expectedOutput;
}

@Parameters
public static Collection<Object[]> testConditions() {
Object expectedOutputs[][] = {
{ "23", false },
{ "34", true },
{ "1", false },
{ "10", true }};
return Arrays.asList(expectedOutputs);
}

@Test
public void checkIsEvenWithValidNumberString() {
MathUtils aMathUtils=new MathUtils();
assertEquals(expectedOutput,aMathUtils.isEven(input));
}

}

As shown above in order to create a parameterized test you first mark the class with the @RunWith(Parameterized.class) annotation. Then you need a static method with a @Parameters annotation which will return a collection of array which will have your input/expected-output values. Above we have just one input, that’s why our array is of size 2. Now we have to map these input/expected-output to class variables so that test method may use them. So create these class variables. In order to populate them for each value, JUnit need a constructor to populate, hence create a constructor to do the same. Lets run

As you can see we have only 1 test method but it ran 4 times based on the parameter we sent.

 

Test Suites

Often time it is required to run multiple classes together, without having to run each individually. This can be done via Suite in JUnit. Lets see this by an example MathUtilsTestSuite.java


package com.mynotes.testing;

import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;

@RunWith(Suite.class)
@SuiteClasses({MathUtilsTest.class,
MathUtilsParameterizedTest.class})
public class MathUtilsTestSuite {

}

As you can see, above we first told JUnit to run this class with Suit – @RunWith(Suite.class). Then @SuiteClasses we told which all classes needs to run together. Running

Categories

In JUnit, you can organise the test cases into different categories, and run those categorised test cases with @ExcludeCategory or @IncludeCategory. Categories are similar to Test Suites which can run many classes together in a single suite. But a category runner is special kind of suite runner that can read @Category annotation over methods and classes and run a specific kind of category/categories only (as mentioned). Lets see this by an example. First lets create a category class – MathUtilsValidTestCategory.java


package com.mynotes.testing;

public class MathUtilsValidTestCategory {

}

Creating a class with more valid tests and annotating it with @Category(MathUtilsValidTestCategory.class)


package com.mynotes.testing;

import static org.junit.Assert.assertEquals;

import org.junit.Test;
import org.junit.experimental.categories.Category;

@Category(MathUtilsValidTestCategory.class)
public class MathUtilsMoreValidTests {

@Test
public void checkIsEvenWithValidNumberString() {
MathUtils aMathUtils=new MathUtils();
assertEquals(true,aMathUtils.isEven("10"));
}

@Test
public void checkIsEvenWithInvalidNumberString() {
MathUtils aMathUtils=new MathUtils();
assertEquals(false,aMathUtils.isEven("33"));
}

}

Lets create another class MathUtilsTests and mark only one method mapped to @Category(MathUtilsValidTestCategory.class)


package com.mynotes.testing;

import static org.junit.Assert.assertEquals;

import org.junit.Test;
import org.junit.experimental.categories.Category;

public class MathUtilsTests {

@Test
@Category(MathUtilsValidTestCategory.class)
public void checkIsEvenWithValidNumberString() {
MathUtils aMathUtils=new MathUtils();
assertEquals(true,aMathUtils.isEven("222"));
}

@Test
public void checkIsEvenWithInvalidNumberString() {
MathUtils aMathUtils=new MathUtils();
assertEquals(false,aMathUtils.isEven("3 3"));
}

@Test(expected=NumberFormatException.class)
public void checkIsEvenWithInvalidString() {
MathUtils aMathUtils=new MathUtils();
assertEquals(true,aMathUtils.isEven("qq"));
}

}

Now, creating valid category suite – MathUtilsValidTestSuite.java


package com.mynotes.testing;

import org.junit.experimental.categories.Categories;
import org.junit.experimental.categories.Categories.IncludeCategory;
import org.junit.runner.RunWith;
import org.junit.runners.Suite.SuiteClasses;

@RunWith(Categories.class)
@IncludeCategory(MathUtilsValidTestCategory.class)
@SuiteClasses({MathUtilsTests.class,
MathUtilsMoreValidTests.class})
public class MathUtilsValidTestSuite {

}

Above with @RunWith(Categories.class) we told JUnit to run it with category runner. Then with @IncludeCategory we specify which all category should be included. (There is also an @ExcludeCategory). Then with @SuiteClasses we told JUnit which all classes needs to considered and searched for the mentioned categories. Running it

%d bloggers like this: