Input validation
Testing the happy path is often a straightforward endeavor. But how the code responds to unexpected inputs should also be defined and tested.
Take for example the following class which accepts two strings as input, converts them to integers, then adds the two together and returns the result as a string so it can also be set in a TextView
to display the result.
public class AddingMachine {
public String sum (String a, String b) {
int sumInt = Integer.parseInt(a) + Integer.parseInt(b);
return Integer.toString(sumInt);
}
}
The following test case is quite sufficient for testing expected inputs.
public class AddingMachineTest {
private AddingMachine addingMachine = new AddingMachine(); @Test
public void sum_shouldReturnResultAsString() throws Exception {
assertThat(addingMachine.sum("2", "3")).isEqualTo("5");
}
}
But what about unexpected inputs?
What if one of the parameters is an empty string or null
? What if the string cannot be converted to an integer? This behavior should also be defined and tested.
One approach is to throw an exception for non-conforming input. This puts the responsibility on the calling class to validate the input before it is passed as a parameter. In this case we can verify the correct exception is thrown in the tests.
@Test(expected = IllegalArgumentException.class)
public void sum_shouldThrowForNullParam() throws Exception {
addingMachine.sum(null, null);
}@Test(expected = IllegalArgumentException.class)
public void sum_shouldThrowForEmptyParam() throws Exception {
addingMachine.sum("", "");
}@Test(expected = IllegalArgumentException.class)
public void sum_shouldThrowForNonNumberParam() throws Exception {
addingMachine.sum("a", "b");
}
But perhaps it would be possible to handle invalid input more elegantly. In this instance the result will be set to a TextView
so returning null
for invalid input would clear the result field when TextView.setText(null)
is invoked.
@Test public void sum_shouldReturnNullForNullAddend()
throws Exception {
assertThat(addingMachine.sum(null, null)).isNull();
}@Test public void sum_shouldReturnNullForEmptyAddend()
throws Exception {
assertThat(addingMachine.sum("", "")).isNull();
}@Test public void sum_shouldReturnNullForNonNumberAddend()
throws Exception {
assertThat(addingMachine.sum("a", "b")).isNull();
}
Letting this new set of input validation tests guide the implementation here is the AddingMachine
class in its final form.
public class AddingMachine {
public String sum (String a, String b) {
if (a == null || b == null) {
return null;
} if (a.length() == 0 || b.length() == 0) {
return null;
} int sumInt; try {
sumInt = Integer.parseInt(a) + Integer.parseInt(b);
} catch (NumberFormatException e) {
return null;
} return Integer.toString(sumInt);
}
}
It now correctly handles all sorts of edge case inputs and still returns a useful result when called even if it is not possible to sum the two input values.