Test Doubles و Fake در PHP — mock، stub، spy و fake درست
همه جایگزینهای test را «mock» مینامند — اما معنای دقیق فرق دارد. انتخاب double اشتباه تست شکننده یا بیفایده میسازد. در Laravel بخش بزرگی با fake داخلی حل میشود؛ بقیه Mockery یا fake دستی.
انواع Test Double
| نوع | نقش | مثال |
|---|---|---|
| Dummy | پر کردن پارامتر — استفاده نمیشود | object خالی |
| Stub | پاسخ از پیش تعیین | return 42 |
| Spy | فراخوانی واقعی + ثبت | shouldHaveReceived |
| Mock | assert expectation قبل از اجرا | shouldReceive once |
| Fake | implementation سبک کارا | Http::fake |
Fake در Laravel — ترجیح اول
Http::fake([
'api.zarinpal.com/*' => Http::response(['data' => ['authority' => 'A000']], 200),
]);
Mail::fake();
Storage::fake('s3');
Queue::fake();
Event::fake();
نگهداری شده، readable — Feature test.
Fake سفارشی برای interface
class InMemoryPostRepository implements PostRepositoryInterface
{
private array $posts = [];
public function create(array $data): Post
{
$post = new Post($data);
$post->id = count($this->posts) + 1;
$this->posts[] = $post;
return $post;
}
}
// test
$this->app->instance(PostRepositoryInterface::class, new InMemoryPostRepository());
fake واقعیتر از mock — رفتار قابل پیشبینی. Repository.
Stub با Mockery
$gateway = Mockery::mock(PaymentGateway::class);
$gateway->shouldReceive('charge')->andReturn(['ok' => true]);
// بدون assert تعداد — فقط setup
Mock — verify رفتار
$gateway->shouldReceive('charge')->once()->with(5000);
کی Fake نه Mock؟
- سرویس Laravel داخلی — Mail، Http، Notification
- نیاز به state ساده in-memory
- چند متد همکاری میکنند
کی Mock؟
- فقط verify یک فراخوانی boundary
- external SDK بدون fake رسمی
- exception path
Anti-pattern: mock database layer کامل
Eloquent mock نکنید — RefreshDatabase یا SQLite in-memory.
Fake clock
Carbon::setTestNow('2026-01-15 10:00:00');
// test scheduled publish
Carbon::setTestNow();
جمعبندی
Laravel fake → custom fake → mock/spy. Action test: Repository Action. CI: Coverage.