phpunit mock method multiple calls with different arguments Ask Question

phpunit mock method multiple calls with different arguments Ask Question

Is there any way to define different mock-expects for different input arguments? For example, I have database layer class called DB. This class has method called Query(string $query), that method takes an SQL query string on input. Can I create mock for this class (DB) and set different return values for different Query method calls that depends on input query string?

ベストアンサー1

It's not ideal to use at() if you can avoid it because as their docs claim

The $index parameter for the at() matcher refers to the index, starting at zero, in all method invocations for a given mock object. Exercise caution when using this matcher as it can lead to brittle tests which are too closely tied to specific implementation details.

Since 4.1 you can use withConsecutive eg.

$mock->expects($this->exactly(2))
     ->method('set')
     ->withConsecutive(
         [$this->equalTo('foo'), $this->greaterThan(0)],
         [$this->equalTo('bar'), $this->greaterThan(0)]
       );

If you want to make it return on consecutive calls:

  $mock->method('set')
         ->withConsecutive([$argA1, $argA2], [$argB1], [$argC1, $argC2])
         ->willReturnOnConsecutiveCalls($retValueA, $retValueB, $retValueC);

PHPUnit 10 removed withConsecutive. You can get similar functionality with:

$mock->expects($this->exactly(2))
    ->method('set')
    ->willReturnCallback(fn (string $property, int $value) => match (true) {
        $property === 'foo' && $value > 0,
        $property === 'bar' && $value > 0 => $mock->$property = $value,
        default => throw new LogicException()
    });

Obviously way uglier and not quite the same, but that's the state of things. You can read more about alternatives here: https://github.com/sebastianbergmann/phpunit/issues/4026 and here: https://github.com/sebastianbergmann/phpunit/issues/4026#issuecomment-825453794

おすすめ記事