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.