Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 680
slim Avatar asked Oct 28 '16 16:10

slim


Video Answer


1 Answers

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();
            }
         }
       };
    }
}
like image 165
developer Avatar answered Sep 19 '22 14:09

developer