A simple Java banking library built as a hands-on introduction to unit testing with JUnit 5.
The focus is on learning testing concepts — not building a full app. The codebase is intentionally small so you can understand every line.
| Concept | Where to find it |
|---|---|
@Test, @BeforeEach, @AfterEach |
BankAccountTest — top of file |
assertEquals, assertTrue |
DepositTests, TransactionHistoryTests |
assertThrows — testing exceptions |
WithdrawalTests, TransferTests |
| Inspecting exception fields | withdraw_moreThanBalance_shouldThrowInsufficientFunds |
@ParameterizedTest + @ValueSource |
DepositTests, WithdrawalTests |
@Nested for grouping tests |
All sections in BankAccountTest |
@DisplayName for readable output |
Every test |
| Testing state doesn't change on failure | transfer_onFailure_shouldNotChangeAnyBalance |
| Testing immutability | transactionHistory_shouldBeUnmodifiable |
fintech-bank/
├── src/
│ ├── main/java/com/fintech/
│ │ ├── exception/
│ │ │ └── InsufficientFundsException.java
│ │ └── model/
│ │ ├── BankAccount.java
│ │ └── Transaction.java
│ └── test/java/com/fintech/
│ ├── exception/
│ │ └── InsufficientFundsExceptionTest.java
│ └── model/
│ └── BankAccountTest.java ← Start here
├── pom.xml
└── README.md
- Java 11+
- Maven 3.6+
mvn testmvn test -Dtest=BankAccountTestmvn test -Dtest=BankAccountTest#deposit_validAmount_shouldIncreaseBalance@BeforeEach // runs before every @Test — use for fresh setup
@AfterEach // runs after every @Test — use for cleanup
@BeforeAll // runs once before all tests in the class (must be static)
@AfterAll // runs once after all tests in the class (must be static)assertEquals(expected, actual) // values are equal
assertNotEquals(unexpected, actual) // values are not equal
assertTrue(condition) // condition is true
assertFalse(condition) // condition is false
assertNull(object) // object is null
assertNotNull(object) // object is not null
assertThrows(Exception.class, () -> { // code throws the right exception
// code that should throw
});// Run the same test with multiple inputs
@ParameterizedTest
@ValueSource(doubles = {0, -1, -999.99})
void myTest(double value) {
// runs 3 times, once per value
}@Nested
@DisplayName("Deposit tests")
class DepositTests {
@Test void someTest() { ... }
}When you have time to continue, here are natural next steps:
- SavingsAccount — extends
BankAccount, adds interest rate, testapplyInterest() - AccountService — handles multiple accounts, test
findByAccountNumber() - Transaction filtering — filter history by type or date range, test edge cases
- Overdraft protection — allow a configurable negative balance limit, update tests
-
@ParameterizedTestwith@CsvSource— test deposit + expected balance pairs - Mockito — mock dependencies when you're ready to move past pure unit tests