laravel 模拟——迹忆客-ag捕鱼王app官网
简介
测试 laravel 应用的时候,你可能还想要“ 模拟 ”应用的特定状态,以便在测试中不让它们真的执行。例如,测试触发事件的控制器时,你可能想要模拟事件监听器以便它们不在测试期间真的执行。这样的话你就可以只测试控制器的 http 响应,而不必担心事件监听器的执行,因为事件监听器可以在它们自己的测试用例中被测试。
laravel 为模拟事件、任务以及 facade 提供了辅助函数,这些辅助函数主要是在 mockery
之上提供了一个方便的层这样你就不必手动调用复杂的 mockery
方法。当然,我们也可以使用 mockery
或 phpunit
来创建自己的模拟。
模拟对象
模拟对象可以通过 laravel 的服务容器注入到应用中,你需要通过 instance 方法将模拟实例绑定到容器中,这将会告知容器使用模拟的对象实例而不是真正的实例自身:
use mockery;
use app\service;
$this->instance(service::class, mockery::mock(service::class, function ($mock) {
$mock->shouldreceive('process')->once();
}));
为了让代码更简洁,可以使用 laravel 测试基类提供的 mock
方法:
use app\service;
$this->mock(service::class, function ($mock) {
$mock->shouldreceive('process')->once();
});
当我们只需要模拟对象的几个方法时,您可以使用partialmock
方法。未被模拟的方法在调用时会正常执行:
use app\service;
$this->partialmock(service::class, function ($mock) {
$mock->shouldreceive('process')->once();
});
类似的,如果想要暗中监控某个对象,laravel 测试基类还提供了 spy 方法,该方法提供了对 mockery::spy 方法的封装:
use app\service;
$this->spy(service::class, function ($mock) {
$mock->shouldhavereceived('process');
});
伪造 bus
作为模拟的替代方案,你可以使用 bus facade 的 fake
方法来阻止任务被分发,使用 fake
的时候,测试代码执行后会进行断言:
order->id === $order->id;
});
// assert a job was not dispatched...
bus::assertnotdispatched(anotherjob::class);
}
}
伪造事件
作为模拟的替代方案,我们可以使用 event facade 的 fake
方法来阻止事件监听器被执行,然后断言事件被分发,甚至检查接收的数据。使用 fake
方法时,测试代码执行后会进行断言:
order->id === $order->id;
});
// assert an event was dispatched twice...
event::assertdispatched(ordershipped::class, 2);
// assert an event was not dispatched...
event::assertnotdispatched(orderfailedtoship::class);
}
}
注:调用 event:fake() 后,事件监听器不会执行,因此,如果你的测试使用了依赖于事件的模型工厂,例如在模型 creating 事件中创建一个 uuid,那么你需要在使用工厂后再调用 event::fake()。
伪造事件子集
如果只想要为指定的事件集合伪造事件监听器,可以将它们传递到 fake
或 fakefor
方法:
/**
* test order process.
*/
public function testorderprocess()
{
event::fake([
ordercreated::class,
]);
$order = factory(order::class)->create();
event::assertdispatched(ordercreated::class);
// other events are dispatched as normal...
$order->update([...]);
}
有作用域的事件伪造
如果只想为部分测试伪造事件监听器,可以使用 fakefor
方法:
create();
event::assertdispatched(ordercreated::class);
return $order;
});
// events are dispatched as normal and observers will run ...
$order->update([...]);
}
}
伪造邮件
我们可以使用 mail facade 的 fake
方法阻止邮件发送,然后断言发送给用户的可邮寄类,甚至检查接收的数据。使用 fake
的时候,断言会在测试代码执行后进行:
order->id === $order->id;
});
// assert a message was sent to the given users...
mail::assertsent(ordershipped::class, function ($mail) use ($user) {
return $mail->hasto($user->email) &&
$mail->hascc('...') &&
$mail->hasbcc('...');
});
// assert a mailable was sent twice...
mail::assertsent(ordershipped::class, 2);
// assert a mailable was not sent...
mail::assertnotsent(anothermailable::class);
}
}
如果将邮件发送推送到了后台异步队列,需要使用 assertqueued
来替代 assertsent
:
mail::assertqueued(...);
mail::assertnotqueued(...);
伪造通知
你可以使用 notification 门面的 fake 方法来阻止通知被发送,之后断言通知是否被发送给用户,甚至可以检查接收的数据。使用 fake 的时候,断言会在测试代码执行后进行:
order->id === $order->id;
}
);
// assert a notification was sent to the given users...
notification::assertsentto(
[$user], ordershipped::class
);
// assert a notification was not sent...
notification::assertnotsentto(
[$user], anothernotification::class
);
// assert a notification was sent via notification::route() method...
notification::assertsentto(
new anonymousnotifiable, ordershipped::class
);
// assert notification::route() method sent notification to the correct user...
notification::assertsentto(
new anonymousnotifiable,
ordershipped::class,
function ($notification, $channels, $notifiable) use ($user) {
return $notifiable->routes['mail'] === $user->email;
}
);
}
}
伪造队列
作为模拟的替代方案,可以使用 queue facade
的 fake
方法来阻止任务被推动到队列,然后断言任务是否被推送到队列,甚至检查接收的数据。使用 fake
的时候,断言会在测试代码执行后进行:
order->id === $order->id;
});
// assert a job was pushed to a given queue...
queue::assertpushedon('queue-name', shiporder::class);
// assert a job was pushed twice...
queue::assertpushed(shiporder::class, 2);
// assert a job was not pushed...
queue::assertnotpushed(anotherjob::class);
// assert a job was pushed with a given chain of jobs, matching by class...
queue::assertpushedwithchain(shiporder::class, [
anotherjob::class,
finaljob::class
]);
// assert a job was pushed with a given chain of jobs, matching by both class and properties...
queue::assertpushedwithchain(shiporder::class, [
new anotherjob('foo'),
new finaljob('bar'),
]);
// assert a job was pushed without a chain of jobs...
queue::assertpushedwithoutchain(shiporder::class);
}
}
伪造存储
storage facade
的 fake
方法允许我们轻松构造伪造硬盘,以及使用uploadedfile
类生成的文件,从而极大简化了文件上传测试,例如:
json('post', '/photos', [
uploadedfile::fake()->image('photo1.jpg'),
uploadedfile::fake()->image('photo2.jpg')
]);
// assert one or more files were stored...
storage::disk('photos')->assertexists('photo1.jpg');
storage::disk('photos')->assertexists(['photo1.jpg', 'photo2.jpg']);
// assert one or more files were not stored...
storage::disk('photos')->assertmissing('missing.jpg');
storage::disk('photos')->assertmissing(['missing.jpg', 'non-existing.jpg']);
}
}
注:默认情况下,
fake
方法会删除临时目录下的所有文件,如果你想要保留这些文件,可以使用persistentfake
方法。
facade
不同于传统的静态方法调用,facade 可以被模拟。这与传统静态方法相比是一个巨大的优势,并且你可以对依赖注入进行测试。测试的时候,你可能经常想要在控制器中模拟 laravel facade 的调用,例如,看看下面的控制器动作:
我们可以通过使用 shouldreceive
方法模拟 cache facade
的调用,该方法返回一个mockery模拟的实例,由于facade通过 laravel 服务容器进行解析和管理,所以它们比通常的静态类更具有可测试性。例如,我们可以来模拟 cache facade get
方法的调用:
once()
->with('key')
->andreturn('value');
$response = $this->get('/users');
// ...
}
}
注:不要模拟 request facade
,取而代之地,可以在测试时传递期望输入到 http 辅助函数如 get
和 post
,类似地,也不要模拟 config facade,在测试中调用 config::set
方法即可。
查看笔记