Search code examples
javajunitparameterized

Inheritance (or an alternative) in Parameterized jUnit tests


I'd like to do something like this in jUnit:

@Runwith(Parameterized.class)
public abstract class BaseTest {

     protected abstract List<Object[]> extraParams();
     protected abstract ClassUnderTest testObject;

     @Parameters
     public Collection<Object[]> data() {
         List<Object> params = ...; // a standard set of tests
         params.addAll(extraParams());
         return params;
     }

     @Test
     public doTest() {
         // assert things about testObject
     }
}

public class ConcreteTest extends BaseTest {
     protected ClassUnderTest = new ConcreteClass(...);
     protected List<Object[]) extraParams() {
         List<Object> extraParams = ...; // tests specific to this concrete type
         return extraParams;
     }
}

So that by extending this class, I run a bunch of standard tests against the object under test, plus some extra ones specified in the concrete class.

However, jUnit requires that the @Parameters method is static. How else can I tidily achieve the aim, of having a set of standard parameters plus extra ones in the concrete classes?

The best I've come up with so far is to have an un-annotated Collection<Object[]> standardParams() in the abstract class, and to require that the subclass contain a method:

 @Parameters
 public Collection<Object[]> data() {
     List<Object> params = standardParams();
     params.addAll(...); // extra params
     return params;
 }

... but this isn't as tidy as I'd like, as it puts too much responsibility on the writer of the subclass.


Solution

  • JUnit expects that the @Parameters method must be static and if you don't provide the static method, it throws No public static parameters method on class Exception.

    But your requirement can be acheived by implementing org.junit.rules.TestRule as below:

    BaseTest class

    public abstract class BaseTest {
    
        @Rule
        public MyBaseTestRule myProjectTestRule = new MyBaseTestRule(data());
    
        protected abstract List<Object[]> extraParams();
    
        public List<Object[]> data() {  
            List<Object[]> listTotal = new ArrayList<>();
            listTotal.addAll(extraParams());
            //add your base test data here
            return listTotal;
        }
    
        public abstract List<Object[]> extraParams();
    }
    

    ConcreteTest class

    public class ConcreteTest extends BaseTest  {
    
        @Override
        public List<Object[]> extraParams() {
            List<Object[]> list = ...//set up data here
            return list;
        }   
    
        @Test
        public void test1() {
            Object[] testData = myProjectTestRule.getTestData();
            //use the test data for the test
            //Example: Assume addition of two integers scenario and data 
            //data[0] expectedresult, data[1],[2] inputs
            //Assert.assertEquals((int)data[0], (int)(data[1]+data[2]));
    
        }
    
        //add other test cases
    }
    

    MyBaseTestRule class:

    import java.util.List;
    
    import org.junit.rules.TestRule;
    import org.junit.runner.Description;
    import org.junit.runners.model.Statement;
    
    public class MyBaseTestRule implements TestRule { 
    
        private final List<Object[]> totalTestData;
    
        private final int totalTestsSize;
    
        private int currentTestIndex;
    
        public MyProjectTestRule(List<Object[]> list) {
          this.totalTestsSize = list.size();
          this.totalTestData = list;
       }
    
       public Object[] getTestData(){
          return totalTestData.get(currentTestIndex);
       }
    
       @Override
       public Statement apply(Statement stmt, Description desc) {
    
          return new Statement() {
    
             @Override
             public void evaluate() throws Throwable {
                for(int i=0; i<totalTestsSize; i++) {
                    currentTestIndex = i;
                    stmt.evaluate();
                }
             }
           };
        }
    }