Add junit-mockito module: 30 runnable JUnit 6.1.3 / Mockito 5.24.0 interview answers

One test per question, each writing and asserting its own transcript under output/.
Covers lifecycle, extensions, parameterized and dynamic tests, mocks vs spies,
verification, strict stubs, static and constructor mocking, and flaky tests.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01CifR2BE7PBvRy5hT8TS9oz
This commit is contained in:
2026-10-26 01:52:44 +00:00
co-authored by Claude Sonnet 5.5
parent d03962ea9c
commit 715036b640
57 changed files with 1672 additions and 0 deletions
@@ -0,0 +1,6 @@
TestEngines found on the classpath (ServiceLoader<TestEngine>):
junit-jupiter 6.1.3
Jupiter API version : 6.1.3
Platform launcher : 6.1.3
Platform engine API : 6.1.3
junit-vintage engine : not on the classpath
@@ -0,0 +1,2 @@
junit-jupiter-api 5.14.4 : class-file major version 52 (Java 8), MethodOrderer$Alphanumeric entries in jar: 1
junit-jupiter-api 6.1.3 : class-file major version 61 (Java 17), MethodOrderer$Alphanumeric entries in jar: 0
@@ -0,0 +1,3 @@
class-file major version of org.junit.jupiter.api.Test : 61 (Java 17)
Class.forName("org.junit.jupiter.api.MethodOrderer$Alphanumeric") -> ClassNotFoundException
(the 5.14.4 versus 6.1.3 jar comparison is scripts/compare-5-vs-6.sh -> q02-jar-comparison.txt)
@@ -0,0 +1,8 @@
@BeforeAll (static)
@BeforeEach on instance #1
test second() on instance #1
@AfterEach on instance #1
@BeforeEach on instance #2
test first() on instance #2
@AfterEach on instance #2
@AfterAll (static)
@@ -0,0 +1,3 @@
@BeforeAll (non-static) on instance #1
second(): instance #1, counter=1
first(): instance #1, counter=2
@@ -0,0 +1,8 @@
outer @BeforeEach
outerTest()
outer @AfterEach
outer @BeforeEach
inner @BeforeEach
innerTest()
inner @AfterEach
outer @AfterEach
@@ -0,0 +1,4 @@
declared order in the source : zebra(), apple(), mango(), banana()
default (no orderer configured) : [banana(), apple(), mango(), zebra()]
MethodOrderer.MethodName : [apple(), banana(), mango(), zebra()]
MethodOrderer.DisplayName : [apple(), banana(), mango(), zebra()]
@@ -0,0 +1,5 @@
assertAll reported 2 failures from 3 assertions:
order (2 failures)
org.opentest4j.AssertionFailedError: quantity ==> expected: <1> but was: <2>
org.opentest4j.AssertionFailedError: code ==> expected: <A> but was: <B>
assertThrows returns the exception: getMessage() = boom
@@ -0,0 +1,4 @@
assertTimeout runs the code on the test thread : true
assertTimeoutPreemptively runs the code on the test thread : false
assertTimeout failure message (trimmed at ' by '): execution exceeded timeout of 50 ms
assertTimeoutPreemptively failure message: execution timed out after 50 ms
@@ -0,0 +1,3 @@
assertionFails() -> FAILED
assumptionFails() -> ABORTED
passes() -> SUCCESSFUL
@@ -0,0 +1,8 @@
declared: beforeAll
declared: beforeEach
test class: @BeforeEach method
declared: beforeTestExecution
test class: the test body
declared: afterTestExecution
declared: afterEach
declared: afterAll
@@ -0,0 +1,13 @@
declared: beforeAll
programmatic(static field): beforeAll
declared: beforeEach
programmatic(static field): beforeEach
declared: beforeTestExecution
programmatic(static field): beforeTestExecution
test body
programmatic(static field): afterTestExecution
declared: afterTestExecution
programmatic(static field): afterEach
declared: afterEach
programmatic(static field): afterAll
declared: afterAll
@@ -0,0 +1,2 @@
custom parameter : Tenant[name=tenant-for-resolvesCustomAndBuiltIn]
built-in TestInfo: displayName=resolvesCustomAndBuiltIn(Tenant, TestInfo)
@@ -0,0 +1,12 @@
enumSource(Level) / [1] LOW -> SUCCESSFUL
enumSource(Level) / [2] HIGH -> SUCCESSFUL
valueSource(int) / [1] 1 -> SUCCESSFUL
valueSource(int) / [2] 2 -> SUCCESSFUL
valueSource(int) / [3] 3 -> SUCCESSFUL
nullAndEmpty(String) / [1] null -> SUCCESSFUL
nullAndEmpty(String) / [2] "" -> SUCCESSFUL
nullAndEmpty(String) / [3] " " -> SUCCESSFUL
csvSource(String, int) / [1] "apple", "1" -> SUCCESSFUL
csvSource(String, int) / [2] "banana", "2" -> SUCCESSFUL
methodSource(String, int) / [1] "x", 10 -> SUCCESSFUL
methodSource(String, int) / [2] "y", 20 -> SUCCESSFUL
@@ -0,0 +1,11 @@
display name: csv(String, String) / [1] a="x" b=null -> SUCCESSFUL
display name: csv(String, String) / [2] a=null b="y" -> SUCCESSFUL
display name: csv(String, String) / [3] a="" b="z" -> SUCCESSFUL
display name: csv(String, String) / [4] a=null b="w" -> SUCCESSFUL
display name: csv(String, String) / [5] a="a,b" b="q" -> SUCCESSFUL
--- values the test method received ---
a="x" b=null
a=null b="y"
a="" b="z"
a=null b="w"
a="a,b" b="q"
@@ -0,0 +1,4 @@
@BeforeEach ran (1 time so far)
squares() / square of 1 -> SUCCESSFUL
squares() / square of 2 -> SUCCESSFUL
squares() / square of 3 -> SUCCESSFUL
@@ -0,0 +1,7 @@
--- no filter ---
disabled() -> SKIPPED (waiting for the fix in ticket 1234)
fast() -> SUCCESSFUL
onlyWithProperty() -> SKIPPED (System property [lab.run.slow] does not exist)
slow() -> SUCCESSFUL
--- includeTags("fast") ---
fast() -> SUCCESSFUL
+2
View File
@@ -0,0 +1,2 @@
inside the test : dir exists = true, files = 1
after the test finished : dir exists = false
+2
View File
@@ -0,0 +1,2 @@
mock: next() -> 0, next() -> 0, real code ran 0 times
spy : next() -> 1, next() -> 2, real code ran 2 times
@@ -0,0 +1,3 @@
when(spy.next()).thenReturn(100): real method ran while stubbing = 1 time(s)
doReturn(100).when(spy).next() : real method ran while stubbing = 0 time(s)
both return 100 and 100 after stubbing
@@ -0,0 +1,14 @@
times / atLeastOnce / never / inOrder: all passed
--- verify(gw).lookup("zzz") ---
Wanted but not invoked:
paymentGateway.lookup("zzz");
--- verify(gw).audit("declined c") ---
Argument(s) are different! Wanted:
paymentGateway.audit("declined c");
-> at lab.jm.D_MockitoCoreTest.lambda$q20_verify$1(D_MockitoCoreTest.java:90)
Actual invocations have different arguments:
paymentGateway.charge("a", 500L);
-> at lab.jm.OrderService.place(OrderService.java:17)
--- verify(gw, never()).audit("declined b") ---
paymentGateway.audit("declined b");
Never wanted here:
@@ -0,0 +1,5 @@
when(gw.charge("a", anyLong())) -> InvalidUseOfMatchersException
Invalid use of argument matchers!
2 matchers expected, 1 recorded:
-> at lab.jm.D_MockitoCoreTest.lambda$q21_matchersAndCaptor$0(D_MockitoCoreTest.java:103)
ArgumentCaptor saw account=zoe cents=1234
+11
View File
@@ -0,0 +1,11 @@
MockitoExtension (default strictness):
unusedStub() -> FAILED
org.mockito.exceptions.misusing.UnnecessaryStubbingException
Unnecessary stubbings detected.
Clean & maintainable test code requires zero unnecessary code.
Following stubbings are unnecessary (click to navigate to relevant line of code):
1. -> at lab.jm.fixtures.MockitoFixtures$StrictByDefault.unusedStub(MockitoFixtures.java:25)
Please remove unnecessary stubbings or use 'lenient' strictness. More info: javadoc for UnnecessaryStubbingException class.
@MockitoSettings(strictness = LENIENT):
unusedStub() -> SUCCESSFUL
@@ -0,0 +1,4 @@
consecutive stubbing: true, false
third call throws : gateway down
void method via doThrow: audit store full
thenAnswer / BDD willAnswer: lookup("7") = user-7
@@ -0,0 +1,5 @@
FinalPricer is final: true
mock(FinalPricer.class).price(3) = 999 (real code would give 30)
mock.getClass() == FinalPricer.class : true
default MockMaker : org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker
class-file major version of org.mockito.Mockito : 55 (Java 11)
+4
View File
@@ -0,0 +1,4 @@
before : REAL-ID
inside the scope : MOCK-ID
other thread sees : REAL-ID
after the scope : REAL-ID
@@ -0,0 +1,4 @@
before : RECEIPT[tea]
inside : FAKE [tea]
constructed during the scope: 1
after : RECEIPT[tea]
@@ -0,0 +1,5 @@
Mockito is currently self-attaching to enable the inline-mock-maker. This will no longer work in future releases of the JDK. Please add Mockito as an agent to your build as described in Mockito's documentation: https://javadoc.io/doc/org.mockito/mockito-core/latest/org.mockito/org/mockito/Mockito.html#0.3
WARNING: A Java agent has been loaded dynamically (~/.m2/repository/net/bytebuddy/byte-buddy-agent/1.17.7/byte-buddy-agent-1.17.7.jar)
WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading to hide this warning
WARNING: If a serviceability tool is not in use, please run with -Djdk.instrument.traceUsage for more information
WARNING: Dynamic loading of agents will be disallowed by default in a future release
@@ -0,0 +1,6 @@
int -> 0
boolean -> false
String -> null
List<String> -> []
Optional<String> -> Optional.empty
long -> 0
@@ -0,0 +1,10 @@
--- order a_, b_ (alphabetical) ---
a_registersUser() -> SUCCESSFUL
b_assumesUserExists() -> SUCCESSFUL
--- order b_, a_ (reversed) ---
b_assumesUserExists() -> FAILED
a_registersUser() -> SUCCESSFUL
AssertionFailedError: registry should contain the user registered by another test ==> expected: <1> but was: <0>
--- time: the same code with an injected Clock ---
acceptsOrders() at 10:00 UTC : true
acceptsOrders() at 23:30 UTC : false
@@ -0,0 +1,13 @@
failsOnThirdRepetition(RepetitionInfo) / repetition 1 of 6 -> SUCCESSFUL
failsOnThirdRepetition(RepetitionInfo) / repetition 2 of 6 -> SUCCESSFUL
failsOnThirdRepetition(RepetitionInfo) / repetition 3 of 6 -> FAILED
failsOnThirdRepetition(RepetitionInfo) / repetition 4 of 6 -> SKIPPED (Failure threshold [1] exceeded)
failsOnThirdRepetition(RepetitionInfo) / repetition 5 of 6 -> SKIPPED (Failure threshold [1] exceeded)
failsOnThirdRepetition(RepetitionInfo) / repetition 6 of 6 -> SKIPPED (Failure threshold [1] exceeded)
withoutThreshold(RepetitionInfo) / repetition 1 of 6 -> SUCCESSFUL
withoutThreshold(RepetitionInfo) / repetition 2 of 6 -> SUCCESSFUL
withoutThreshold(RepetitionInfo) / repetition 3 of 6 -> FAILED
withoutThreshold(RepetitionInfo) / repetition 4 of 6 -> SUCCESSFUL
withoutThreshold(RepetitionInfo) / repetition 5 of 6 -> SUCCESSFUL
withoutThreshold(RepetitionInfo) / repetition 6 of 6 -> SUCCESSFUL
failed=2 skipped=3