Skip to content

understudySpecify the call by making it.

No method names in strings. One engine, two runner adapters and two static analysers.

understudy logo

Five packages, one engine ​

  • understudy — the engine: doubles, stubbing, expectations, verification, transcripts. Depends on nothing but PHP.
  • understudy-testo — verification and reset wired into Testo's lifecycle, with Fiber isolation.
  • understudy-phpunit — the same, through a trait; works under Pest.
  • understudy-psalm — a Psalm plugin that types the specification closure and reports misuse.
  • understudy-phpstan — the same checks as PHPStan rules, under the understudy.* identifiers.

Install the engine plus exactly the adapter your suite already uses. The static analysers are independent of both: either one can be added on its own.

The message tells you which one it was ​

An object argument is matched by identity, so two instances never match however equally they read. The message has to be able to say which of the two reasons a call failed — and it does:

text
Understudy `BookRepository` expected `save(App\Book#1 {title: 'Dune'})` to be called
exactly 2 times, but it was called 1 time.

The following calls to `save` were made during this test:
    save(App\Book#1 {title: 'Dune'})
    save(*App\Book#2 {title: 'Dune'}*)

#1 and #2 are aliases numbered within one message, in order of first appearance — not object ids, which are reused after a collection and would print differently on the next run. The asterisks mark what differed.

See the Cookbook for this and four other real incidents, each with a script you can run.