Introduction to Testing in Java

1 minute read

“It works on my machine” is not a testing strategy. Automated testing is how professional developers ensure their code works correctly, catches regressions, and can be refactored safely.

In this chapter, you’ll learn why testing matters, the testing pyramid, and the anatomy of a good test.


Why Testing Matters

  1. Catch bugs early — find issues before users do
  2. Refactor safely — change code with confidence that nothing breaks
  3. Documentation — tests show how your code is supposed to behave
  4. Design feedback — hard-to-test code is usually poorly designed
  5. CI/CD enabler — automated tests are required for continuous deployment

The Testing Pyramid

        ╱╲
       ╱  ╲      E2E Tests (Selenium, Playwright)
      ╱    ╲     — Slow, expensive, few
     ╱──────╲
    ╱        ╲   Integration Tests (Testcontainers, @SpringBootTest)
   ╱          ╲  — Medium speed, test component interactions
  ╱────────────╲
 ╱              ╲ Unit Tests (JUnit 5, Mockito)
╱________________╲— Fast, cheap, many

Write mostly unit tests, some integration tests, and few end-to-end tests.


Anatomy of a Test: Arrange-Act-Assert

Every good test follows the AAA pattern:

@Test
void shouldCalculateTotalPrice() {
    // Arrange — set up test data
    ShoppingCart cart = new ShoppingCart();
    cart.addItem(new Item("Book", 29.99));
    cart.addItem(new Item("Pen", 4.99));

    // Act — call the method under test
    double total = cart.getTotal();

    // Assert — verify the result
    assertEquals(34.98, total, 0.01);
}

Test Naming Conventions

Good test names describe the scenario and expected outcome:

// ✅ Good — describes behavior
void shouldReturnZero_WhenCartIsEmpty()
void shouldThrowException_WhenItemIsNull()
void shouldApplyDiscount_WhenCouponIsValid()

// ❌ Bad — describes implementation
void testGetTotal()
void test1()

What’s Next?

In the next chapter, you’ll set up JUnit 5 and write your first automated tests.

Updated: