本文是我在實踐後的一點總結,不免有不妥之處。若有幸得大神路過,還望不吝賜教,小弟在此謝過了!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單元測試與測試驅動開發,受益不淺。