Testing / JUnit6 Interview Questions
What are best practices and anti-patterns in JUnit 6 test design?
Writing maintainable, fast, and reliable tests requires following established principles. Knowing common anti-patterns is as important as knowing the APIs.
| Category | Best practice | Anti-pattern to avoid |
|---|---|---|
| Test isolation | Each test should set up its own state independently | Relying on test execution order or shared mutable state between tests |
| Assertion messages | Use lazy message suppliers: assertEquals(a, b, () -> 'msg') | Eager string concatenation in message: assertEquals(a, b, 'msg: ' + val) -- always evaluated |
| Exception testing | Use assertThrows() and verify the message | @Test(expected=) which doesn't let you inspect the exception |
| Grouping | Use assertAll() for multi-field object validation | Multiple separate assertions -- first failure hides others |
| Naming | Use @DisplayName or @DisplayNameGeneration for readable names | cryptic method names like testMethod1() |
| Extension reuse | Extract cross-cutting concerns into extensions or shared flows | Copy-pasting @BeforeEach setup across test classes |
| Parallel safety | Use @ResourceLock for shared resources | Shared mutable static state without synchronisation |
| Temporary files | Use @TempDir(cleanup=ON_SUCCESS) for debuggable failures | Manual file management in @BeforeEach/@AfterEach |
// ANTI-PATTERN: order-dependent tests @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class BadTest { static String sharedState; @Test @Order(1) void setup() { sharedState = "ready"; } // BAD @Test @Order(2) void useIt() { assertEquals("ready", sharedState); } } // BEST PRACTICE: independent setup per test class GoodTest { @BeforeEach void init() { this.state = buildTestState(); } // each test gets fresh state @Test void test1() { assertEquals("ready", state.status()); } @Test void test2() { assertEquals("ready", state.status()); } // also passes } // ANTI-PATTERN: testing multiple concerns in one test @Test void testEverything() { // Tests create, update, delete, and serialise all at once // When it fails, you don't know WHAT broke } // BEST PRACTICE: one concern per test @Test void createOrder_succeeds() { ... } @Test void updateOrder_changesStatus() { ... } @Test void deleteOrder_removesFromStore() { ... }
More Related questions...