Intro
D'accord, je vois qu'une solution est fournie pour Mockery, donc comme je n'aime pas Mockery, je vais vous donner une alternative à Prophecy mais je vous suggère d'abord de lire la différence entre Mockery et Prophecy.
En bref : "La prophétie utilise une approche appelée liaison de message - cela signifie que le comportement de la méthode ne change pas avec le temps, mais est plutôt changé par l'autre méthode."
Code problématique du monde réel à couvrir
class Processor
{
    /**
     * @var MutatorResolver
     */
    private $mutatorResolver;
    /**
     * @var ChunksStorage
     */
    private $chunksStorage;
    /**
     * @param MutatorResolver $mutatorResolver
     * @param ChunksStorage   $chunksStorage
     */
    public function __construct(MutatorResolver $mutatorResolver, ChunksStorage $chunksStorage)
    {
        $this->mutatorResolver = $mutatorResolver;
        $this->chunksStorage   = $chunksStorage;
    }
    /**
     * @param Chunk $chunk
     *
     * @return bool
     */
    public function process(Chunk $chunk): bool
    {
        $mutator = $this->mutatorResolver->resolve($chunk);
        try {
            $chunk->processingInProgress();
            $this->chunksStorage->updateChunk($chunk);
            $mutator->mutate($chunk);
            $chunk->processingAccepted();
            $this->chunksStorage->updateChunk($chunk);
        }
        catch (UnableToMutateChunkException $exception) {
            $chunk->processingRejected();
            $this->chunksStorage->updateChunk($chunk);
            // Log the exception, maybe together with Chunk insert them into PostProcessing Queue
        }
        return false;
    }
}
Solution PhpUnit Prophecy
class ProcessorTest extends ChunkTestCase
{
    /**
     * @var Processor
     */
    private $processor;
    /**
     * @var MutatorResolver|ObjectProphecy
     */
    private $mutatorResolverProphecy;
    /**
     * @var ChunksStorage|ObjectProphecy
     */
    private $chunkStorage;
    public function setUp()
    {
        $this->mutatorResolverProphecy = $this->prophesize(MutatorResolver::class);
        $this->chunkStorage            = $this->prophesize(ChunksStorage::class);
        $this->processor = new Processor(
            $this->mutatorResolverProphecy->reveal(),
            $this->chunkStorage->reveal()
        );
    }
    public function testProcessShouldPersistChunkInCorrectStatusBeforeAndAfterTheMutateOperation()
    {
        $self = $this;
        // Chunk is always passed with ACK_BY_QUEUE status to process()
        $chunk = $this->createChunk();
        $chunk->ackByQueue();
        $campaignMutatorMock = $self->prophesize(CampaignMutator::class);
        $campaignMutatorMock
            ->mutate($chunk)
            ->shouldBeCalled();
        $this->mutatorResolverProphecy
            ->resolve($chunk)
            ->shouldBeCalled()
            ->willReturn($campaignMutatorMock->reveal());
        $this->chunkStorage
            ->updateChunk($chunk)
            ->shouldBeCalled()
            ->will(
                function($args) use ($self) {
                    $chunk = $args[0];
                    $self->assertTrue($chunk->status() === Chunk::STATUS_PROCESSING_IN_PROGRESS);
                    $self->chunkStorage
                        ->updateChunk($chunk)
                        ->shouldBeCalled()
                        ->will(
                            function($args) use ($self) {
                                $chunk = $args[0];
                                $self->assertTrue($chunk->status() === Chunk::STATUS_PROCESSING_UPLOAD_ACCEPTED);
                                return true;
                            }
                        );
                    return true;
                }
            );
        $this->processor->process($chunk);
    }
}
Résumé
Encore une fois, Prophecy est plus impressionnant! Mon astuce est de tirer parti de la nature de liaison de messagerie de Prophecy et même si cela ressemble malheureusement à un code d'enfer javascript de rappel typique, commençant par $ self = $ this; comme vous devez très rarement écrire des tests unitaires comme celui-ci, je pense que c'est une bonne solution et c'est vraiment facile à suivre, à déboguer, car il décrit en fait l'exécution du programme.
BTW: Il existe une deuxième alternative mais nécessite de changer le code que nous testons. Nous pourrions envelopper les fauteurs de troubles et les déplacer dans une classe distincte:
$chunk->processingInProgress();
$this->chunksStorage->updateChunk($chunk);
pourrait être enveloppé comme:
$processorChunkStorage->persistChunkToInProgress($chunk);
et c'est tout mais comme je ne voulais pas créer une autre classe pour cela, je préfère la première.