Monday, January 20, 2014

Parameterised tests with TestNG

My previous posts on this blog have both been about how wonderful it is to be able to run exactly the same tests against client and server side code. However, actually doing that is a little bit clunky in JUnit, requiring you to create a separate test method for each implementation of your interface. At best you can do this:
@Test
public void testGetCustomerById_Service() {
    doTestGetCustomerById(service);
}

@Test
public void testGetCustomerById_Client() {
    doTestGetCustomerById(client);
}

public void doTestGetCustomerById(CustomerService service) {
    int id = random.nextInt();
        
    Customer expected = new Customer(id);
    when(customerDao.getById(id)).thenReturn(expected);
        
    Customer actual = service.getCustomerById(id);
    assertEquals(expected, actual);
}
Your choice is between writing 3 methods for each test, or 2 methods containing identical code. Here's something better you can do if you're using TestNG.
@Test(dataProvider = "myProvider")
public void testGetCustomerById(CustomerService service) {
    int id = random.nextInt();
        
    Customer expected = new Customer(id);
    when(customerDao.getById(id)).thenReturn(expected);
        
    Customer actual = service.getCustomerById(id);
    assertEquals(expected, actual);
}

@DataProvider(name = "myProvider")
public Object[][] provider() {
    return new Object[][] {{service}, {client}};
}
That Object[][] looks a bit ugly, but the results are great. Now you have 1 method per test, plus 1 data provider method for the whole test class. The first dimension of the returned array represents each run of the test, while the second allows you to pass multiple parameters. Here's a (slightly contrived) example testing a calculator implementation.
@Test(dataProvider = "myProvider")
public void testAddition(int a, int b, int expectedResult) {
    int result = calculator.calc(a + "+" + b);
    assertEquals(expectedResult, result);
}

@DataProvider(name = "myProvider")
public Object[][] provider() {
    return new Object[][] {{ 1, 2, 3 }, { 4, 5, 9 }, { 10, 11, 21 }};
}
And you can use the same provider to test subtraction.
@Test(dataProvider = "myProvider")
public void testSubtraction(int expectedResult, int b, int a) {
    int result = calculator.calc(a + "-" + b);
    assertEquals(expectedResult, result);
}

No comments: