JUnit & TestNG
The standard Java testing frameworks. JUnit 5 is the default for modern Spring Boot projects; TestNG is the choice where complex parallel execution, data providers, and suite-level configuration are required.
The Hook: The Bedrock of Enterprise
In the world of NZ banking and government IT, Java is the king. Millions of dollars move through systems built on Java every day. You wouldn't trust a bank that didn't test its code, right? JUnit and TestNG are the tools that ensure those millions go to the right place.
If you want to work at Xero, ANZ, or the IRD, knowing how to write and run Java unit tests isn't just a "nice to have" — it's the ticket to the game.
The Rule: Isolate or Fail
The golden rule of Java unit testing is Isolation. Your test must not depend on the file system, the network, or the database. If it does, and the network is down, your test fails even if your code is perfect. That's a "False Negative," and it's the enemy of a fast pipeline.
The Analogy: Foundations vs. Decoration
Imagine building a skyscraper like the Sky Tower. Unit Tests are checking the strength of the individual steel beams. Integration Tests are checking if the beams fit together. E2E Tests are checking if the elevator reaches the top floor.
If the beams (Unit) are weak, the elevator (E2E) will eventually crash, no matter how many times you test the buttons.
The Setup: Maven & Gradle
In Java, we don't just "install" a package; we declare a dependency in our build tool. For Maven (the most common in NZ), you add this to your pom.xml:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
The First Script: Assertions
Java tests use Annotations (the @ symbol) to tell the runner what to do. Here is your first test:
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class CalculatorTest {
@Test
void testAddition() {
int result = 2 + 2;
assertEquals(4, result, "2 + 2 should equal 4");
}
}
The "Gotcha": Flaky Setup
The most common "Gotcha" in Java testing is Shared State. If Test A changes a static variable that Test B relies on, Test B might fail only when run in a certain order. This makes your tests "flaky."
The Fix: Use @BeforeEach. This annotation runs a "cleanup" or "setup" function before every single test, ensuring every test starts with a fresh, clean environment.
The Framework: Power of Annotations
JUnit 5 (Jupiter) gives you a powerful toolkit:
@Test: Marks a method as a test.@ParameterizedTest: Runs the same test with different data.@Disabled: Skips a test (don't use this too often!).@DisplayName: Gives the test a human-readable name in reports.
The NZ Example: IRD Number Validator
Let's look at a classic NZ requirement: validating an IRD number. They must be 8 or 9 digits and follow a specific checksum logic.
@ParameterizedTest
@ValueSource(strings = {"12345678", "123456789"})
void testValidIrdLength(String ird) {
assertTrue(IrdValidator.isValidLength(ird));
}
@Test
void testInvalidIrdChars() {
assertFalse(IrdValidator.isValid("123-456-78")); // No dashes allowed!
}
The Pro Move: Parallel Execution
In large enterprise projects with 10,000+ tests, running them one-by-one takes hours. TestNG was built specifically to solve this with Parallel Execution.
By simply adding parallel="methods" thread-count="5" to your XML config, you can run 5 tests at once, cutting your wait time by 80%. This is how senior leads optimise the CI pipeline for speed.
The Challenge: Your Turn
Write a test class for a BankAccount class. It has a withdraw(amount) method.
Your Task: Write three tests: 1. A successful withdrawal, 2. An attempted withdrawal with insufficient funds (should throw an exception), and 3. A withdrawal of a negative amount (should be rejected).