JUnit for Strong Senior Developers
What are the key differences between JUnit 4 and JUnit 5?
JUnit 5 is composed of three main modules:
JUnit Platform — foundation for launching testing frameworks.
JUnit Jupiter — new programming model and extension model.
JUnit Vintage — supports JUnit 3/4 tests.
Differences:
Annotations: @BeforeEach, @AfterEach (replaces @Before, @After).
More flexible assertions and conditional test execution (e.g., @EnabledOnOs).
Support for parameterized tests, nested tests, and dynamic tests.
How do you write parameterized tests in JUnit 5 and when would you use
them?
Parameterized tests allow testing the same logic with different inputs.
Use @ParameterizedTest with @ValueSource, @CsvSource, or @MethodSource.
Example:
@ParameterizedTest
@ValueSource(ints = {1, 2, 3})
void testWithIntegers(int input) { assertTrue(input > 0); }
Use cases:
Validating logic across a range of values.
Avoiding code duplication in similar test cases.
How does JUnit support test lifecycle management and what are the common
annotations?
JUnit lifecycle annotations:
@BeforeAll — run once before all tests (static).
@BeforeEach — run before each test method.
@AfterEach — run after each test method.
@AfterAll — run once after all tests (static).
Helps in setting up and cleaning up shared resources, database state, mocks, etc.
JUnit 5 also supports @TestInstance(Lifecycle.PER_CLASS) to avoid static setup
methods.
How would you test exception scenarios in JUnit?
Use assertThrows() to test expected exceptions.
Example:
assertThrows(IllegalArgumentException.class, () -> service.process(null));
You can also assert on the message:
Exception ex = assertThrows(...);
assertEquals("Invalid input", ex.getMessage());
Avoid using try-catch in tests — assertThrows is clearer and idiomatic.
How do you structure test code for maintainability and readability in a large
project?
Best practices:
Follow AAA structure — Arrange, Act, Assert.
Use descriptive test method names (e.g., shouldReturnError_WhenInputIsInvalid).
Group tests by functionality or use nested classes.
Isolate test setup using @BeforeEach/@BeforeAll or helper methods.
Avoid shared mutable state to prevent flaky tests.
Maintain consistent naming and documentation for test coverage clarity.