How do slot() and capture() work in MockK, and when would you capture an argument instead of just matching it?
answer
- slot<T>() container, capture(slot), read slot.captured
- captures generated/unpredictable values
- MutableList capture = all calls in order
- every-capture feeds answers { }
- slot holds last value; obeys all-or-nothing rule
basics
~10 sA slot is a container. You pass capture(slot) where an argument goes, and MockK stores the real value the code passed in. Afterwards you read slot.captured to assert on or inspect that value.
solid answer
~50 sslot<T>() creates a CapturingSlot<T>. Inside every or verify you write capture(theSlot) in the argument position; MockK records the actual argument into the slot. You then read theSlot.captured to make assertions about an object your test couldn't construct beforehand — e.g. an entity with a generated id or timestamp. capture follows the same all-or-nothing matcher rule. For multiple invocations, use a MutableList<T> with capture(list) to collect every call's argument in order, then assert on the list. Capturing is preferable to a complex eq() when the expected value is hard to express up front or you only care about a few fields. A subtlety: if both every and verify capture into the same slot, the slot holds the last captured value; and capturing in every runs at call time while capturing in verify runs after the action. Use answers { slot.captured } to echo a captured value back as the stub result.
code
kotlin · 11 linesval saved = slot<User>()
val repo = mockk<UserRepository>(relaxed = true)
UserService(repo).register("Ada")
verify { repo.save(capture(saved)) }
assertEquals("Ada", saved.captured.name)
assertTrue(saved.captured.id > 0)
// Capture all calls:
val ids = mutableListOf<Long>()
verify { repo.findById(capture(ids)) }go deeper
Understands a slot stores an argument you read via slot.captured.
Uses slot() in verify to assert on a generated value and knows the list form for multiple calls.
Distinguishes capture timing in every vs verify and feeds captured values into answers { }; knows slot keeps only the last value.
Guides when capturing improves readability vs. over-coupling tests, and standardizes capture usage in the test suite.
## The problem capturing solves Sometimes you can't write `eq(expected)` because you don't know the exact value in advance — the code under test builds an object with a generated id, a `now()` timestamp, or a derived field. **Capturing** lets you grab the *actual* argument and assert on the parts you care about. ## `slot()` — a single value container `slot<T>()` returns a `CapturingSlot<T>`. You place `capture(slot)` where the argument goes; MockK writes the real argument into the slot. Read it via `slot.captured`. ```kotlin val saved = slot<User>() val repo = mockk<UserRepository>(relaxed = true) UserService(repo).register("Ada") verify { repo.save(capture(saved)) } assertEquals("Ada", saved.captured.name) assertTrue(saved.captured.id > 0) // generated id we couldn't predict ``` `slot.isCaptured` tells you whether anything was captured. `capture()` obeys the **all-or-nothing matcher rule**: if a call uses `capture`, all its other arguments must be matchers too. ## Capturing many calls — `MutableList` For a method called multiple times, capture into a list to keep every argument in order: ```kotlin val ids = mutableListOf<Long>() verify { repo.findById(capture(ids)) } assertEquals(listOf(1L, 2L, 3L), ids) ``` ## Capturing in `every` vs `verify` - In `verify`, capture happens **after** the action runs — you inspect what was passed historically. - In `every`, capture happens **at call time**; you can then use the captured value to compute the stub's result: ```kotlin val slot = slot<User>() every { repo.save(capture(slot)) } answers { slot.captured.copy(id = 99L) } ``` Here `answers { }` reads `slot.captured` to echo back an enriched object — the captured argument feeds the dynamic answer. ## Slot semantics - A single `slot()` holds only the **last** captured value; for history, use the list form. - If both an `every` and a `verify` capture into the same slot, the final read reflects the most recent capture. ## When to capture vs. match - Use `eq()`/`any()` when you can express the expectation directly. - Use `capture()` when the value is generated, large, or you only want to assert on a subset of fields — capturing keeps the test readable instead of building a giant `eq` expected object. ## Pitfall summary - Don't read `slot.captured` before the capturing call actually happened (it throws). - Capturing every argument 'just in case' bloats tests; capture only what you assert on.
- You need every argument of repeated calls, not just the last. How?Pass a MutableList<T> to capture(list); MockK appends each call's argument in order.
- How can a captured value influence the stub's return value?Capture in every and use answers { slot.captured ... } to compute the result from the captured argument.
A slot is like a evidence bag: as the call passes by, you snatch the actual argument into the bag and examine it later.
saying these in an interview costs you the question
- Reading slot.captured before the capturing call ran
- Expecting a single slot to retain every call's value
- Mixing capture with raw literals (breaks all-or-nothing rule)
- Capturing arguments you never assert on
- Confusing capture (records value) with any() (only matches)