Java Unit Testing (JUnit)
In this page:
@Test
void testName() {
// arrange
// act
assertEquals(expected, actual);
}
Assertions
Assertions are how a test actually verifies that code produced the expected result -- methods like assertEquals or assertTrue compare an actual value against an expected one. If the comparison fails, the assertion throws, which JUnit catches and reports as a failed test rather than letting the exception crash the whole run.
Example: Assertions
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
public class Main {
@Test
void testAddition() {
int result = 2 + 3;
assertEquals(5, result); // fails (and is reported) if this doesn't hold
}
public static void main(String[] args) {
new Main().testAddition();
System.out.println("Assertion passed");
}
}
Login to try C/C++/Java/PHP code in the editor
The Test Lifecycle
Because unit tests should run independently of each other and of execution order, JUnit provides lifecycle annotations like @BeforeEach and @AfterEach to prepare a test's dependencies fresh before it runs and clean them up afterward, preventing state from one test from leaking into the next.
Example: The Test Lifecycle
// Import org.junit.jupiter.api.* so it can be used by its short name
import org.junit.jupiter.api.*;
// Define the class Main
public class Main {
@BeforeEach
void setup() { System.out.println("Fresh setup before each test"); }
@AfterEach
void cleanup() { System.out.println("Cleanup after each test"); }
@Test
void sampleTest() { System.out.println("Running test"); }
// Program entry point: the JVM starts running here
public static void main(String[] args) {
// Create a new Main object and store it in m
Main m = new Main();
m.setup(); m.sampleTest(); m.cleanup();
}
}
Login to try C/C++/Java/PHP code in the editor
Testing Exceptions
You should explicitly test that your methods throw the right exceptions when given invalid input, using assertThrows() to assert both that an exception is thrown and that it's the correct type, rather than only testing the happy path and hoping bad input fails loudly elsewhere.
Example: Testing Exceptions
// Import org.junit.jupiter.api.Assertions.* so it can be used by its short name
import static org.junit.jupiter.api.Assertions.*;
// Import org.junit.jupiter.api.Test so it can be used by its short name
import org.junit.jupiter.api.Test;
// Define the class Main
public class Main {
static void divide(int a, int b) {
if (b == 0) throw new ArithmeticException("divide by zero");
}
@Test
void testDivideByZero() {
assertThrows(ArithmeticException.class, () -> divide(10, 0));
}
// Program entry point: the JVM starts running here
public static void main(String[] args) {
new Main().testDivideByZero();
// Print a line to the console
System.out.println("Exception correctly thrown and verified");
}
}
Login to try C/C++/Java/PHP code in the editor
Dynamic Parameterized Tests
Parameterized tests (via @ParameterizedTest) let you run the same test logic repeatedly against a table of different inputs and expected outputs. This is far less repetitive than writing a near-identical test method for every edge case you want to check.
Example: Dynamic Parameterized Tests
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
public class Main {
@ParameterizedTest
@ValueSource(ints = {2, 4, 6})
void testEven(int number) {
System.out.println(number + " is even: " + (number % 2 == 0));
}
public static void main(String[] args) {
Main m = new Main();
for (int n : new int[]{2, 4, 6}) m.testEven(n); // one method, many inputs
}
}
Login to try C/C++/Java/PHP code in the editor
Suite Runners
To run your tests across an entire application or module in one pass, you aggregate individual test classes inside a test suite configuration, which JUnit or your build tool can then execute as a single unit -- useful for CI pipelines that need one pass/fail signal for the whole codebase.
Example: Suite Runners
import org.junit.platform.suite.api.*;
@Suite
@SelectClasses({}) // aggregates individual test classes for one CI pass/fail signal
public class Main {
public static void main(String[] args) {
System.out.println("Suite runs every selected test class as a single unit");
}
}
Login to try C/C++/Java/PHP code in the editor
- Writing
assertEquals(result, 5)in the wrong order, when it isassertEquals(expected, actual), which makes failure messages misleading. - Testing exceptions with a try/catch instead of
assertThrows, so the test passes even when no exception is thrown. - Making tests depend on each other or on execution order through shared state, when each test should run on its own.
- JDBC connects Java programs to databases and performs create, read, update, and delete operations.
- ORM with Hibernate maps Java objects to database tables.
- JUnit provides a way to write unit tests for your code.
Chapter Quiz — Complete all 4 topics to unlock
0/4 topics done
Complete these topics first: