WeCodeAcademy

Step 7: Test the rules

Tests turn your rules into checks that run in a second. This example builds a tiny test framework (a test method that reports pass or fail) so you can see what a test runner does, and tests each rule: new accounts are empty, deposits and withdrawals change the balance, an overdraw is refused and leaves the balance unchanged, and non-positive amounts are rejected. One test is deliberately wrong so you can see how a failure is reported.

ExampleJava
import java.util.*;

public class Main {
  static class Account {
    private long cents;
    void deposit(long amount) {
      if (amount <= 0) throw new IllegalArgumentException("deposit must be positive");
      cents += amount;
    }
    void withdraw(long amount) {
      if (amount <= 0) throw new IllegalArgumentException("withdrawal must be positive");
      if (amount > cents) throw new IllegalStateException("insufficient funds");
      cents -= amount;
    }
    long balance() { return cents; }
  }

  // a tiny test framework: run each test, report pass or fail
  static int failures = 0;
  static void test(String name, Runnable body) {
    try {
      body.run();
      System.out.println("ok   " + name);
    } catch (AssertionError | RuntimeException e) {
      failures++;
      System.out.println("FAIL " + name + " - " + e.getMessage());
    }
  }
  static void check(boolean condition, String message) { if (!condition) throw new AssertionError(message); }
  static void expectThrows(Class<? extends Exception> type, Runnable body) {
    try { body.run(); } catch (Exception e) { if (type.isInstance(e)) return; throw new AssertionError("wrong exception " + e); }
    throw new AssertionError("expected " + type.getSimpleName());
  }

  public static void main(String[] args) {
    test("new account is empty", () -> check(new Account().balance() == 0, "balance should be 0"));
    test("deposit adds to the balance", () -> { Account a = new Account(); a.deposit(500); a.deposit(250); check(a.balance() == 750, "expected 750"); });
    test("withdraw subtracts", () -> { Account a = new Account(); a.deposit(500); a.withdraw(200); check(a.balance() == 300, "expected 300"); });
    test("cannot overdraw", () -> { Account a = new Account(); a.deposit(100); expectThrows(IllegalStateException.class, () -> a.withdraw(101)); check(a.balance() == 100, "balance must not change"); });
    test("rejects non-positive amounts", () -> { Account a = new Account(); expectThrows(IllegalArgumentException.class, () -> a.deposit(0)); expectThrows(IllegalArgumentException.class, () -> a.withdraw(-5)); });
    test("deliberately wrong test", () -> check(new Account().balance() == 1, "balance is 0, not 1"));
    System.out.println(failures == 0 ? "all tests passed" : failures + " test(s) failed");
  }
}
Output
ok   new account is empty
ok   deposit adds to the balance
ok   withdraw subtracts
ok   cannot overdraw
ok   rejects non-positive amounts
FAIL deliberately wrong test - balance is 0, not 1
1 test(s) failed

In a real project you would use JUnit, but the idea is identical. The most valuable tests are on the failure paths: the happy path usually works, bugs hide in what happens when something is refused.

Quick check

Which tests find the most bugs in a bank account class?

Finish the quick check (and any code exercise) to complete this lesson, or mark it done yourself.

Tip: use the left and right arrow keys to move between lessons.