Since most of the testing scenarios data are in the form of table, a Data Driven testing approach can be employed for carrying out unit testing using TestNg/Junit.
For Example,
Consider the example of testing a simple integer division method.
1: Class Divide{
2: public static int divide(int a, int b) {
3: if (b == 0) {
4: throw new ArithmeticException();
5: } else
6: return a / b;
7: }
8: }
For testing this simple method itself properly, we need to test the following scenarios.
Test Case Objective
|
Input number 1
|
Input number 2
|
Expected Result
|
Zero/Zero
|
0
|
0
|
ArithmeticException
|
Zero/positive
|
0
|
2
|
0
|
Zero/negative
|
0
|
-2
|
0
|
Positive/zero
|
5
|
0
|
ArithmeticException
|
Negative/zero
|
-2
|
0
|
ArithmeticException
|
Positive/positive
|
5
|
2
|
2
|
Positive/negative
|
5
|
-2
|
-2
|
Negative/positive
|
-5
|
2
|
-2
|
Negative/negative
|
-5
|
-2
|
2
|
No/same number
|
5
|
5
|
1
|
To test the above scenarios we have to call the same method with different arguments (something like below) or need to have different method for testing each scenario.
1: public class TestDivide {
2:
3: @Test
4: public void testDivide() throws Exception {
5: // Zero/Zero 0 0 ArithmeticException
6: testDivideByZero(0, 0);
7: // Zero/positive 0 2 0
8: assertEquals(0, Divide.divide(0, 2));
9: // Zero/negative 0 -2 0
10: assertEquals(0, Divide.divide(0, -2));
11: // Positive/zero 5 0 ArithmeticException
12: testDivideByZero(5, 0);
13: // Negative/zero -2 0 ArithmeticException
14: testDivideByZero(-2, 0);
15: // Positive/positive 5 2 2
16: assertEquals(2, Divide.divide(5, 2));
17: // Positive/negative 5 -2 -2
18: assertEquals(-2, Divide.divide(5, -2));
19: // Negative/positive -5 2 -2
20: assertEquals(-2, Divide.divide(-5, 2));
21: // Negative/negative -5 -2 2
22: assertEquals(2, Divide.divide(-5, -2));
23: // No/same number 5 5 1
24: assertEquals(1, Divide.divide(5, 5));
25: }
26:
27: private void testDivideByZero(int a, int b) throws Exception {
28: try {
29: Divide.divide(a, b);
30: fail("My method didn't throw when I expected it to");
31: } catch (ArithmeticException e) {
32: } catch (Exception e) {
33: fail("My method throws different Exception when I expect ArithmeticException");
34: }
35: }
36: }
The above approach makes the Test class cumbersome and difficult for other developers to understand the test scenarios.
To avoid this,
We can load the test data from an Excel/CSV/WIKI and validate the output instead of running the same TestNg/Junit test method with different inputs. And also this helps in the Documentation of the test cases that we have already identified and tested.
1: @RunWith(Parameterized.class)
2: public class DataDrivenTestsWithSpreadsheetTest {
3:
4: private int a;
5: private int b;
6: private int aDivideB;
7:
8: @Parameters
9: public static Collection<Object[]> spreadsheetData() throws IOException {
10: InputStream spreadsheet = new FileInputStream("src/test/resources/aDivideB.xls");
11: return new SpreadsheetData(spreadsheet).getData();
12: }
13:
14: public DataDrivenTestsWithSpreadsheetTest(int a, int b, int aDivideB) {
15: super();
16: this.a = a;
17: this.b = b;
18: this.aDivideB = aDivideB;
19: }
20:
21: private void testDivideByZero(int a, int b) throws Exception {
22: try {
23: Divide.divide(a, b);
24: fail("My method didn't throw when I expected it to");
25: } catch (ArithmeticException e) {
26: } catch (Exception e) {
27: fail("My method throws different Exception when I expect ArithmeticException");
28: }
29: }
30:
31: @Test
32: public void shouldCalculateADivideB() {
33: if(b==0){
34: testDivideByZero(a, b);
35: }
36: else
37: assertEquals(aDivideB, Divide.divide(a, b));
38: }
39: }