Mockery::capture를 사용한 결정론적 PHPUnit 어설션 작성
Source: Dev.to
Problem
메서드 내부에서 무작위 값을 생성하면 반환값이 예측할 수 없게 되어 테스트에서 결정론적인(assertion) 검증을 작성하기 어렵습니다.
class Hash
{
public function make($data): string
{
return hash_hmac('sha256', $data, false);
}
}
class RandomHash
{
public function __construct(public Hash $hash)
{
}
/**
* @throws \Exception
*/
public function hash(): string
{
$random = md5(random_int(1, 10));
return $this->hash->make($random);
}
}
random_int가 호출될 때마다 다른 값을 반환하기 때문에 hash()의 결과도 매번 달라집니다. 따라서 테스트에서 고정된 기대값을 검증할 수 없습니다.
class RandomHashTest extends TestCase
{
public function test_mockery_capturing_arguments(): void
{
$hash = new Hash();
$randomHash = new RandomHash($hash);
// 여기서 확정적인(assertion)을 작성할 방법이 없음
$actual = $randomHash->hash();
}
}
Solution
Mockery의 capturing arguments 기능을 사용하면 메서드 호출 시 전달된 인자를 저장할 수 있습니다. 여기에 passthru()(원본 메서드를 정상적으로 실행하도록 하는 메서드)를 결합하면 중간에 생성된 무작위 값을 캡처하고, 이를 기반으로 기대값을 계산할 수 있습니다.
먼저 Mockery를 개발 의존성으로 설치합니다:
composer require --dev mockery/mockery
Example Test
use Mockery;
use PHPUnit\Framework\TestCase;
class RandomHashTest extends TestCase
{
/**
* @throws \Exception
*/
public function test_mockery_capturing_arguments(): void
{
// Hash 클래스에 대한 스파이 생성
$hash = Mockery::spy(new Hash());
// 중간에 생성된 무작위 값을 캡처; passthru()는 make()를 정상적으로 실행하게 함
$hash->allows('make')
->with(Mockery::capture($random))
->passthru();
$randomHash = new RandomHash($hash);
$actual = $randomHash->hash();
// 이제 $random 값이 알려졌으므로 기대값을 계산할 수 있음
self::assertEquals((new Hash)->make($random), $actual);
}
}