C3: Testing Exceptions

CPSC 210

Learning Goals

  • To design tests for methods that throw exceptions

Tests need to deal with them too!

Throwing them in our tests doesn't make much sense

We should catch them!

This way we can also "expect" exceptions

Testing Exceptions

  • There are different ways to test exceptions with jUnit 4/5
    • ​We will focus on a simple (but sufficient) one
/*
 * Withdraw money from the account
 * MODIFIES: this
 * EFFECTS: amount is withdrawn from account and updated
 *            balance is returned
 *            throws InsufficientFundsException if balance<amount
 *            throws NegativeAmountException if amount<0
 */
public double withdraw(double amount) 
    throws InsufficientFundsException, NegativeAmountException {
  • We need at least 3 test cases; one for the case without exceptions and two for the two exceptions

Testing Exceptions (2)

public double withdraw(double amount) 
        throws InsufficientFundsException, NegativeAmountException {
  double initialBalance = balance;
  if (amount < 0) {
    throw new NegativeAmountException();
  }
  if (amount > getBalance()) {
    throw new InsufficientFundsException();
  }
  balance = balance - amount;
  return balance;
}
  • If both conditions are violated we will only receive the first exception!

1

2

Test Case 1: Expect no exception

@Test
void testWithdrawNoException() {
  try {
    testAccount.withdraw(150.50);
  } catch (NegativeAmountException e) {
    fail("Unexpected NegativeAmountException");
  } catch (InsufficientFundsException e) {
    fail("Unexpected InsufficientFundsException");
  }
  assertEquals(349.50, testAccount.getBalance());
}
@Test
void testWithdrawNoException() {
  try {
    testAccount.withdraw(1000);
  } catch (InsufficientFundsException e) {
    fail("Unexpected InsufficientFundsException");
  } catch (NegativeAmountException e) {
    fail("Unexpected NegativeAmountException");
  }
  assertEquals(349.50, testAccount.getBalance());
}
@BeforeEach
void runBefore() {
  testAccount = new Account("Jane", 500.0);
}

Test Case 2: first exception

@Test
void testWithdrawInsufficientFundsException() {
  try {
    testAccount.withdraw(1000);
    fail("InsufficientFundsException was not thrown!");
  } catch (NegativeAmountException e) {
    fail("Unexpected NegativeAmountException");
  } catch (InsufficientFundsException e) {
    // all good!
  }
  assertEquals(500, testAccount.getBalance());
}
@BeforeEach
void runBefore() { testAccount = new Account("Jane", 500.0); }

Test Case 3: second exception

@Test
void testWithdrawNegativeAmountException() {
  try {
    testAccount.withdraw(-500);
    fail("NegativeAmountException was not thrown!");
  } catch (NegativeAmountException e) {
    // all good!
  } catch (InsufficientFundsException e) {
  	fail("Unexpected InsufficientFundsException");
  }
  assertEquals(500, testAccount.getBalance());
}
@BeforeEach
void runBefore() { testAccount = new Account("Jane", 500.0); }

Test Case 4: Violating multiple

if (amount < 0) {
  throw new NegativeAmountException();
}
if (amount > getBalance()) {
  throw new InsufficientFundsException();
}
if (amount % 10 != 0) {
  throw new NotMultipleOfTenException();
}
  • What do we get if we call:
    • withdraw(50)
    • withdraw(600)
    • withdraw(-100)
    • withdraw(-111)
    • withdraw(-600)
    • withdraw(222)
    • withdraw(777) 
@BeforeEach
void runBefore() { testAccount = new Account("Jane", 500.0); }

Assume we had this code

Lecture Ticket Review

// MODIFIES: this 
// EFFECTS: if numThings > 10 throws StressedOutException, otherwise
// does numThings things and returns the value of numThings
int doThings(int numThings) throws StressedOutException;
@Test
public void do3ThingsTestExpectReturn3() {
 int numThingsDone = 0;

 try {
   numThingsDone = doer.doThings(3);
   //POINT F

 } catch (StressedOutException soe) {
   //POINT A
 }
 //POINT B
}
@Test
public void do11ThingsTestExpectStressedOut() {
 int numThingsDone = 0;

 try {
   numThingsDone = doer.doThings(11);
   //POINT E

 } catch (StressedOutException soe) {
   //POINT C
 }
 //POINT D
}

No exception expected!

Exception
expected!

  • Q1: Which would you leave blank (or insert a comment)
  • Q2: Where would you insert a call to the fail method?
  • Q3/4: Where would you insert:
assertEquals(3, numThingsDone);
assertEquals(11, numThingsDone);

Lecture Lab

C3: Testing Exceptions

The End - Thank You!

CPSC210 - C3 Testing Exceptions

By Felix Grund

CPSC210 - C3 Testing Exceptions

  • 1,186