本文是我在实践后的一点总结,不免有不妥之处。若有幸得大神路过,还望不吝赐教,小弟在此谢过了!php
很早就知道有单元测试的概念,也曾尝试过,可是一直对单元测试的概念和方法,比较模糊。在听了 @vimac 大神的讲堂 PHP单元测试与测试驱动开发 后,慢慢地对单元测试和 PHPUnit 的认识清晰了起来,也开始慢慢地去实践单元测试。html
咱们都知道,Laravel 使用了 IoC,各个模块之间也所以解耦了。而正是由于这一点,咱们在 Laravel 中编写单元测试的时候,变得更加轻松了。数据库
考虑如下场景。咱们在开发中,可能会在控制器和模型之间加一个 Repository
来处理数据。那么咱们的 Controller
就会依赖 Respository
。利用 Laravel 的 IoC,咱们能够定义一个 Service Provider
来集中将 Respository
注入到容器中。vim
假设咱们如今有这样一个 Repository,里面记录了商品的信息,咱们想要在 Controller 中获取某件商品信息,而后执行一些业务逻辑。segmentfault
Class GoodRepository { public function getGoodById($goodId) { // TODO: Get good by its id. } } class GoodController extends Controller { public function show($id, GoodRepository $goodRepository) { // TODO: Do something with good info from that repository. } } // In route/api.php Route::get('/api/good/{id}', 'GoodController@show'); // Create a RepositoriesServiceProvider in Provider/RepositoriesServiceProvider.php。 // And inject the GoodRepository into Container. class RepositoriesServiceProvider extends ServiceProvider { public function boot() { } public function register() { $this->app->singleton(GoodRepository::class); } }
好了,咱们能够发现,GoodController
是依赖 GoodRepository
的,而 GoodRepository
是依赖数据库中的数据的。但是咱们在作单元测试的时候,但愿尽量少的产生依赖。因此,咱们应该但愿可以掌控 GoodRepository
所返回的数据。api
在 Laravel,提供了 $this->get('/path/to/route');
的方法来对 HTTP 请求进行测试。这个测试必然会涉及到刚才所提到的那些依赖,如何解决这个依赖的问题,咱们能够请出咱们的主角————桩件。app
将对象替换为(可选地)返回配置好的返回值的测试替身的实践方法称为上桩(stubbing)。ide
这是 PHPUnit 文档上 的解释。那个人理解呢,所谓的桩件,就是模拟一个依赖的类的行为,使得这个行为所作的事情在咱们本身的掌控之中。好比上面的这种状况,咱们但愿模拟 GoodRepository
的 getGoodById
方法返回与真正的返回结构相同的值,而不须要依赖外部数据源。单元测试
咱们经过 Service Provider
注册了 GoodRepository
单例,那么按照这个思路,咱们在写单元测试的时候,就能够将咱们定义的桩件,注册为 GoodRepository
单例。测试
class GoodControllerTest extends TestCase { public function testShow() { $data = []; // The data returns from GoodRepository::getGoodById. $stub = $this->createMock(GoodRepository::class); $stub->method('getGoodById')->will($this->returnValue($data)); $this->app->singleton(GoodRepository::class, function () use ($stub) { return $stub; }); $response = $this->get('/api/good/1'); // Some assertions. } }
咱们经过在这里将桩件 $stub
用单例模式注册给了 Container
,在调用 $this->get('/api/good/1');
时本来在 Controller
中的 GoodRepository
依赖就变成了咱们自定义的桩件 $stub
。咱们将 $data
定义为和返回值相同的结构,注册到桩件中。这样,全部的数据都在咱们可控的范围了。
若是咱们在这里不使用桩件,而是直接依赖外部(数据库)中的数据,那么若是 id 为 1 的数据被删除了,咱们是否是就要改为 2 了呢?咱们是否是就要从新计算数据了匹配断言了呢?这样的测试,可靠性便大大下降。
任何一个可靠的系统,单元测试都是必不可少的。庆幸的是,PHPUnit 帮咱们提供了好用的单元测试。本文所讲的,也只是 PHPUnit 的九牛一毛。而我本身也在慢慢摸索慢慢实践中。与君共勉。
最后仍是推荐去听一下 @vimac 的讲堂 PHP单元测试与测试驱动开发,受益不浅。