Parameterized Unit Tests with JUnit

The current supported parameterized tests in JUnit seemed a little bit confusing for me and tricky to implement, at least compared to the NUnit approach. Below I will show a simple test implementation that asserts the result of the division of a number by another.

The NUnit approach:

using System;

using NUnit.Framework;

namespace JBrisk
{
    [TestFixture]
    public class DivideClassTests
    {
        [TestCase(12, 3, 4)]
        [TestCase(12, 2, 6)]
        [TestCase(12, 4, 3)]
        public void Divide(int n, int d, int q)
        {
            Assert.AreEqual(q, n / d);
        }
    }
}

NICE, NUNIT!

The JUnit approach:

package org.jbrisk.tests;

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

import junit.framework.Assert;

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

@RunWith(Parameterized.class)
public class JUnitDivideClassTests {

	@Parameters
	public static Collection<Object[]> data() {

		return Arrays.asList(new Object[][] { { 12, 3, 4 }, { 12, 2, 6}, { 12, 4, 3 }});
	}

	private int n;
	private int d;
	private int q;

	public JUnitDivideClassTests(int n, int d, int q) {

		this.n = n;
		this.d = d;
		this.q = q;
	}

	@Test
	public void test() {

		Assert.assertEquals(q, n / d);
	}
}

JUNIT, Y U NO MAKE IT SIMPLE?

Don’t get me wrong, I love JUnit and use it daily at my projects, but this parameterized tests feature could be more simple. The reasons I did not like:

  • It needs more 20 lines of code for just a simple test, imagine a more complicated ones.
  • WHAT? I have to create a different class per parameterized test? This breaks a commonly used pattern about “One Test class per Class to Test”.
  • All the additional methods, fields, constructor, reduces the readability and maintainability of the code.

For those out there that do Unit Tests, specially the few ones on their teams/companies that do, you know how hard it is to maintain a lot of tests and even more hard to convince someone to start doing it. Less code (as long as it does not compromise readability) is better!

Well, I didn’t like it! And what my parents taught me a long time ago was:

When you don’t like something, change it!!!

And so I did! Here is the same test with JUnit, but using the @JBriskTestRunner and @ParamTest annotations implemented on my JBrisk project:

package org.jbrisk.tests;

import junit.framework.Assert;

import org.junit.runner.RunWith;

@RunWith(JBriskTestRunner.class)
public class DivideClassTests {


	@ParamTest({ @Values({ "12", "3", "4" }), @Values({ "12", "2", "6" }), @Values({ "12", "4", "3" }) })
	public void test(int q, int n, int d) {

		Assert.assertEquals(q, n / d);
	}
}

The good

  • Reduced the number of lines of code.
  • More readability.
  • Easier to maintain.
  • The implemented runner validates if the length of supplied arguments match the expected parameters length, and also if the supplied values can be converted to the expected type.
  • Since the JBriskTestRunner extends the BlockJUnit4ClassRunner, external tools can execute the parameterized tests (@ParamTest), non-parameterized tests (@Test) and all the other tests supported by it. And also, you don’t have to change your existing test class structure, you only have to add the @RunWith(JBriskTestRunner.class) annotation. Below is a an extended version of the class above and a screenshot of the Eclipse JUnit Test Runner executing its tests:
    package org.jbrisk.tests;
    
    import junit.framework.Assert;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    
    @RunWith(JBriskTestRunner.class)
    public class DivideClassTests {
    
    	@Test
    	public void normalTest() {
    	
    		Assert.assertEquals(1, 1);
    	}
    	
    	@Test(expected = NullPointerException.class)
    	public void testWithExpectedException() {
    		
    		throw new NullPointerException();		
    	}
    	
    	@ParamTest({ @Values({ "12", "3", "4" }), @Values({ "12", "2", "6" }), @Values({ "12", "4", "3" }) })
    	public void divideTest(int n, int d, int q) {
    
    		Assert.assertEquals(q, n / d);
    	}
    }
    

    JBrisk Parameterized Tests on Eclipse

    Notice how the Test Runner view above shows all the 3 runs of the parameterized test, showing also the arguments supplied to each one.

The bad

  • As stated here on the java docs, annotations do not support Object arrays, so, to support tests that receive arguments from different types, I had to resort to an “untyped” String array.
  • But fear not, if the JBriskTestRunner cannot convert the supplied argument to the correct parameter type, it will throw a descriptive error, something like: “The value “StringValue” supplied for the argument at idx “1” cannot be parsed to int! Check the inner exception for details.”

  • Java annotations only support primitive types, Class and enums (or arrays of the mentioned). JBriskTestRunner currently supports:
    • byte/Byte
    • char/Character
    • boolean/Boolean
    • short/Short
    • int/Int
    • long/Long
    • float/Float
    • double/Double
    • String
    • Class
  • You need to add another reference to the JBrisk project. Its not that bad because the JBrisk project does not have dependencies and supports Maven! 😀

One thing i do want to mention is that the JUnit contributors did an AWESOME job on the Runners object model. It was really easy for me to implement this feature because of that. Thank you! 😀

5 comments so far

  1. snakeg68266 on

    Have you look at JUnit’s Theory ?

    • Rafael Ribeiro on

      I did not knew until now :). I searched for it and saw the implementation. Its an interesting approach but I prefer my port of the NUnit Parameterized Unit Tests because is more readable. I can look the Test method and know exactly what is being supplied as arguments, don’t need to look to another method to now. I also saw that the Theory implementation allows to provide complex objects (not only primitive types) as arguments, its nice :). Maybe I will add this funcionality to JBrisk Parameterized Tests also. Thanks for the tip.

  2. piotrek on

    check for zohhak.googlecode.com it supports objects and lets you type much less code

    • Rafael Ribeiro on

      Thanks for the tip, it seems an interest feature. 🙂

  3. Kril on

    I like this tip of making the unit test stand out when running multiple tests in single go. Since I have set up categories of test cases in my project, I hope the paramterized feature of Junit test cases shall also work with @Include/Exclude Category annotation.


Leave a comment