解決したいエラー
public function validate(string $type_name, mixed $input_value, Closure $error): void
{
if (...) {
$error('エラーメッセージ');
}
}
上記コードのUnitテストを書く際に、
$fail を Mockery でモックすると以下のようなエラーになる。
The class \Closure is marked final and its methods cannot be replaced.
結論
Closureは final クラスだから、通常の方法ではモックできない。
なぜモックできないのか?
① ClosureはPHPの内部クラス
・final が付いている
・継承できない
・メソッドを差し替えできない
final class Closure
② Mockeryの仕組み
Mockeryは基本的に:
「元のクラスを継承して、メソッドを上書きする」
ことでモックを作ります。
しかし、final クラスは継承できません。
そのため以下のモックは失敗します。
Mockery::mock(Closure::class);
ではどうテストするのか?
Closureをモックするのではなく、
コールバックの副作用を検証する
のが正解です。
例:フラグで検証する方法
・$fail が呼ばれたらフラグを立てる
・フラグをアサートする
$error_called = false;
$error = function ($message) use (&$error_called) {
$error_called = true;
};
$xxx->validate('name属性', 'input値', $fail);
$this->assertTrue($error_called);
Unitテストの観点
Unitテストは
「ロジックが正しく分岐したか」
を確認するもの。
Closureは分岐結果を外に伝えるための仕組みなので、
・呼ばれたかどうか
・渡された値が正しいか
を検証すれば十分。
| 項目 | 結論 |
|---|---|
| Closureはモックできる? | 通常はできない |
| 理由 | finalクラスだから |
| 正しいテスト方法 | 副作用(フラグ・値)を検証 |