11

Is there any way to avoid calling populateRandomData() method at the begining of each test without having a fixed parameter 100. I need to call the same method to setup data before execution of each test but I need to change the number of test data entries e.g. 100 in each case .

public class Tester
{

   @Before
    public void setUp() {
        populateRandomData(100)
    }

    @Test
    public void testMethod() {

    }

    private void populateRandomData(n){
        //n times insert random data in table.
    }
}
Neil
  • 5,919
  • 15
  • 58
  • 85
  • You mean one test method needs another argument for the call to `populateRandomData` as another test method? Is this still local to the test class? And ... based on what does the argument change? – Seelenvirtuose Oct 14 '14 at 09:56
  • @Seelenvirtuose yes its a local utility method. added the code in the question. – Neil Oct 14 '14 at 09:59
  • 7
    Just invoke `populateRandomData(xxx)` in each test – talex Oct 14 '14 at 10:02
  • @NeilGhosh Is each test doing something different with the random data? If not, perhaps this could be a parameterized test where the only thing that changes is the number of random data objects. Otherwise, I struggle to see a better solution than calling `populateRandomData()` from every test. – Duncan Jones Oct 14 '14 at 10:08
  • @Duncan yes each test is different and tests different functionality – Neil Oct 14 '14 at 10:17
  • @talex I know , but the question here is about possibility of avoiding repeating code. – Neil Oct 14 '14 at 10:18
  • 1
    @NeilGhosh You need some way for each test to express how much random data it needs. I struggle to think of a simpler way than having them call `populateRandomData()` each time. Unless you want to split your test class into multiple classes, extending from a common parent that requires the number of data items as a constructor argument. – Duncan Jones Oct 14 '14 at 10:19
  • @Duncan I tried to have a public class level member annotated with @ Rule but it game me runtime error that The @ Rule 'n' must implement MethodRule or TestRule.Not sure what that means. By definition rules should help us modify them within the test for better flexibility. – Neil Oct 14 '14 at 10:35
  • @NeilGhosh *@ Rule 'n' must implement MethodRule or TestRule.* The meaning of this error is, you haven't created a class which implements `TestRule` interface. – OO7 Oct 14 '14 at 10:54
  • @OO7 Yes that's what it says :) – Neil Oct 14 '14 at 11:17
  • I assume the argument `n` to `populateRandomData()` is specific to each `testMethod()` and not just random. – clD Oct 14 '14 at 11:28

2 Answers2

4

You can create Parameterized JUnit Test which allows you to add number of parameters you want to pass in unit test case. Have a look at example tutorial Create Parameterized Test Case.

OR

@Rule, using this annotations on your test methods to parameterize the execution of your rules makes it even more useful. Taken from JUnit 4.7 @Rules

EDIT :

Example of Using @Rule :

Below is the class which allows you to initialize different value of num variable which will be used in test method :

import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

public class Test1 implements TestRule {

    private final int   num;

    public Test1(int num) {
        this.num = num;
    }

    public int getNum() {
        return num;
    }

    public class Test1Statement extends Statement {
        private final Statement statement;

        public Test1Statement(Statement statement, int num) {
            this.statement = statement;
        }

        @Override
        public void evaluate() throws Throwable {
            statement.evaluate();
        }
    }

    @Override
    public Statement apply(Statement statement, Description description) {
        return new Test1Statement(statement, num);
    }
}

The class below is the actual test case class. It contains JUnit test cases & set value of num variable in test method.

import org.junit.Rule;
import org.junit.Test;

public class RuleNumberTester {

    @Rule
    public Test1    test    = null;

    @Rule
    public Test1    test1   = null;

    @Test
    public void num1Test() {
        test = new Test1(111);
        System.out.println("Num 1 : " + test.getNum());
    }

    @Test
    public void num2Test() {
        test1 = new Test1(222);
        System.out.println("Num 2 : " + test1.getNum());
    }
}

Output :

Test cases are executed successfully & shows the values of num variable which was initialized in test methods on console.

Num 1 : 111
Num 2 : 222
OO7
  • 2,785
  • 1
  • 21
  • 33
  • Note that I don't want the same test to be run with different data more than one time. I want different tests to use a common setup code before and after but the setup varies a bit. – Neil Oct 14 '14 at 10:16
  • @NeilGhosh I have updated my answer with example code for using @ Rule. Have a look it & let me know whether it is useful for you. – OO7 Oct 14 '14 at 11:56
0

I suppose you could use a @Rule to ensure populateRandomData() is called each time with the correct parameters.

However, this gets ugly quickly since you then need to maintain a list of test method names.

private static final Map<String, Integer> dataCounts = new HashMap<>();

static {
  // list (or otherwise obtain) counts here
  dataCounts.put("testMethod", 100);
}

@Rule
public TestWatcher watcher = new TestWatcher() {
  @Override
  protected void starting(Description description) {
    Integer count = dataCounts.get(description.getMethodName());
    assertNotNull(count);
    populateRandomData(count.intValue());
  };
};
Duncan Jones
  • 67,400
  • 29
  • 193
  • 254
  • Hmm , looks like its not worth for this simple case, I would just go for calling the setup method explicitly in every test for now. – Neil Oct 14 '14 at 11:17