Article start
Java Programming: From Zero to Enterprise
4. Advanced Java & Frameworks

40. Unit Testing with JUnit

Proving your code works before you push it to production.

February 22, 2026·69

[!NOTE] If you build an ecommerce banking application, how do you know the "Calculate Interest" function actually works? Do you manually spin the app up, click 50 buttons, input numbers, and check the math yourself? What if someone breaks the function a month from now?

Professional engineers don't test manually. They write code that automatically tests their code. The industry standard framework for this is JUnit.

What is Unit Testing?

A unit is the absolute smallest piece of testable logic in an application—usually a single method.

Imagine you have a straightforward math class:

public class FinancialMath {
    public int calculateTax(int revenue, int deductions) {
        if(deductions > revenue) return 0;
        return (revenue - deductions) * 20 / 100; // 20% tax rate
    }
}

Writing Your First JUnit Test

You create a completely entirely separate file (usually in a /test or /test/java directory).

You annotate methods with @Test. When you click "Run Tests", the IDE executes these methods and uses Assert.assertEquals() to guarantee the output matches what you theoretically expect.

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class FinancialMathTest {

    // 1. Decorate with the Test runner tag!
    @Test
    public void testStandardTaxProcessing() {
        FinancialMath mathSystem = new FinancialMath();
        
        // 2. Perform the action
        int result = mathSystem.calculateTax(1000, 200); 
        // 800 taxable amount * 20% = 160 expected!
        
        // 3. ASSERTION! (Expected Output vs Reality)
        // If 'result' is anything other than 160, the JVM flags this build as FAILED RED!
        assertEquals(160, result, "Basic math calculation failed!");
    }

    @Test
    public void testMassiveDeductionsEdgeCase() {
        FinancialMath mathSystem = new FinancialMath();
        
        // Test an edge case where a user tries to glitch the system with negative taxes
        int result = mathSystem.calculateTax(500, 9999); 
        
        // The business rule states output should hard floor to 0!
        assertEquals(0, result, "Floor mechanism failed! Issued negative tax!");
    }
}

Test-Driven Development (TDD)

Advanced teams use TDD. This means they actually write the JUnit test before they even write the FinancialMath class!

  1. Write the JUnit test for a theoretical tax method.
  2. Run it. It fails violently (Red).
  3. Switch files and write the Java implementation code specifically aiming to make the test Green.
  4. Run it again. Success (Green)!
  5. Refactor the code to be cleaner, knowing the safety net of the Green test is guarding you.

[!IMPORTANT] Congratulations! You have completed the epic 40-chapter journey of mastering the JVM. From writing your very first public static void main statement, all the way to Thread Pools, HashMaps, Spring Boot API Servers, and JUnit pipelines. You are officially ready to embark on a career in backend engineering.

Good Tests Check Behavior, Not Implementation

A useful unit test describes what the code should do from the outside. It should not depend too heavily on private helper methods or internal steps. That way, you can refactor implementation without rewriting every test.

Arrange, Act, Assert

@Test
void calculatesDiscountedPrice() {
    // Arrange
    PricingService service = new PricingService();

    // Act
    double result = service.applyDiscount(1000, 10);

    // Assert
    assertEquals(900, result);
}

Test Edge Cases

  • Normal valid input.
  • Minimum and maximum values.
  • Empty input.
  • Invalid input.
  • Boundary values such as exactly passing marks.

Common Mistakes

  • Writing tests that only cover the happy path.
  • Putting multiple unrelated behaviors into one test.
  • Depending on test execution order.
  • Making tests slow by touching real external services unnecessarily.

Mini Practice

Write tests for a isPassing(mark) method. Cover marks 39, 40, 100, and an invalid negative mark.

Practice Lab: Test Passing Marks

Write small tests around behavior and edge cases.

  1. Create a method isPassing(int mark).
  2. Write a JUnit test for mark 39.
  3. Write a test for mark 40.
  4. Write a test for mark 100.
  5. Decide what should happen for negative marks and test that behavior.

Goal: Practice boundary-value testing, not only happy-path testing.

Revision Checkpoint

  • Unit test: Tests a small piece of behavior.
  • @Test: Marks a test method.
  • Assertion: Compares expected result with actual result.
  • AAA: Arrange, Act, Assert.
  • Edge cases: Test boundaries, invalid input, and normal input.

Before the quiz: Name three test cases for a passing-marks method.