Skip to content

Capturing arguments ​

Arg::captor() is the typed replacement for reading args[N] out of the call log: no positional index, no mixed, no instanceof narrowing ritual.

php
$options = Arg::captor(DeliveryOptions::class);      // Captor<DeliveryOptions>

when(fn () => $store->temporaryUrl(Arg::any(), Arg::any(), $options->capture()))
    ->returns('https://…');

$subject->run();

$options->last()->downloadName;   // typed: DeliveryOptions
$options->all();                  // list<DeliveryOptions>, in call order

capture() goes where the argument to observe goes.

How it matches ​

FormMatches like
Arg::captor(SomeClass::class)Arg::instanceOf(SomeClass::class)
Arg::captor()Arg::any()

The value is recorded only once the whole specification matched the call. A call that the other arguments rejected captures nothing — the captor holds what the specification claimed, not everything that went past it.

Where it works ​

In when(), expect() and verify() alike. A verify() captures from the calls it just claimed, which is the Mockito reading.

Not inside a protocol step

A capture() inside an expectSequence() step matches but does not record. Capture at declaration or at verification, not in a protocol.

Not inside a combinator

A captor inside Arg::allOf(), anyOf(), not() or containing() is refused where it is written. It used to be accepted, and then matched without ever recording: recording happens once the whole specification matched, for the captors the specification holds in a position of their own. The specification was correct in every observable way except the one it was written for, and the only way to find out was an assertion on an empty captor further down the test. For an ordered history across several arguments, read Understudy::calls() instead.

Reading it ​

CallOn an empty captor
last()raises NothingCaptured
all()answers an empty list

The asymmetry is deliberate: last() is asked when the test believes a call happened, so silence there would be a false green; all() is asked to iterate, where an empty list is a perfectly ordinary answer.

Lifetime ​

Captured values live exactly as long as the call log. reset() and a closing Understudy::scope() drop them, and the captor object is then simply empty again.