Example: Invalid Cases and Boundary Cases
There is one invalid case that the previous tests did not cover: the case that the denominator is zero. A fraction must have a non-zero integer as the denominator.
To write a test to ensure that this error is handled properly, you must first define the expected behavior. While there’s not exactly one right way to handle this, there are definitely wrong ways to handle this. Your code should not just replace the 0 with another number; that just leads to tricky logic errors. Your code should not ask the user to provide a different value; asking for input does not align with previous behavior and the user would not expect your class to suddenly expect input.
Your code should fail at this point. That is, some sort of exception should be raised. Exceptions are very obvious and help the user debug their programs. There is no logical way for your code to proceed at this point, so it should not proceed.
There are two ways to test that an exception has been thrown. You could use a try/catch block and purposefully fail a test if the exception wasn’t thrown. TestNG also has annotations for this scenario. The code below demonstrates how to write a test that expects an exception to be thrown.
// Test Fraction constructor fails when denominator is zero @Test(expectedExceptions=ArithmeticException.class) public void testConstructor() { // create a test object Fraction testObj = new Fraction(1, 0); // if the exception is thrown, the test passes // if the exception is not thrown, the test fails }
As written, the Fraction class actually fails this test. We need to go back and fix the constructor:
public Fraction(int top, int bottom) { if(bottom == 0) throw new ArithmeticException("Denominator cannot be zero"); numerator = top; denominator = bottom; reduce(); }
Good thing we tested with invalid data!