Laravel – Unit Test 單元測試教學
Laravel 整合了 phpunit 製作單元/功能測試,我們以空專案 blog 來進行範例。
1 2 3 4 |
cd blog composer create-project --prefer-dist laravel/laravel . |
資料庫設定
寫測試的時候,建立一份專門提供給測試的資料庫,避免與實際運作的開發環境共用。根目錄底下,複製一份 .env 為 .env.testing 並修改內容,運行測試的時候透過 artisan 指令添加參數 –env=testing 將會覆蓋 .env 的值
1 2 3 4 5 6 7 |
// .env.testing .... DB_DATABASE=測試資料庫名稱 DB_USERNAME=測試使用者帳號 DB_PASSWORD=測試使用者密碼 |
修正 DB 支援的問題
若出現 Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes 那麼請修改 app/Providers/AppServiceProvider.php
1 2 3 4 5 6 7 8 |
use Illuminate\Support\Facades\Schema; public function boot() { Schema::defaultStringLength(191); } |
建立 Migration
一開始先建立 DB Schema
1 2 3 |
php artisan make:migration create_posts_table |
1 2 3 4 5 6 7 8 9 10 11 12 |
// database\migrations\2019_08_19_071005_create_posts_table.php public function up() { Schema::create('posts', function (Blueprint $table) { $table->bigIncrements('id'); $table->string('title', 50); $table->longText('content'); $table->timestamps(); }); } |
記住,我們要運行在測試資料庫
1 2 3 4 |
php artisan config:clear // 記得清除快取,使之可以讀取 testing php artisan migrate --env=testing |
建立資料表 Posts
1 2 3 |
php artisan make:model Post |
建立測試資料工廠
這裡的工廠 PostFactory 負責定義我們的假資料工廠,只要返回我們需要的欄位即可
1 2 3 |
php artisan make:factory PostFactory --model=Post |
1 2 3 4 5 6 7 8 9 |
// database\factories\PostFactory.php $factory->define(Post::class, function (Faker $faker) { return [ 'title' => $faker->word, 'content' => $faker->paragraph ]; }); |
建立單元測試
我們在單元測試中會做這幾件事情
- 建立兩筆資料
- 驗證是否符合預期
- 刪除測試資料
1 2 3 |
php artisan make:test PostTest --unit |
可以建立多個方法來進行測試,命名的規則例如
- testExample() 在測試顯示時的名稱叫做 Example
- testUserRegister() 在測試顯示時的名稱叫做 User register
我們嘗試編寫
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// tests\Unit\PostTest.php use App\Post; class PostTest extends TestCase { // 可以重整資料表,避免測試資料不斷累積。注意這會刪除所有資料,不要用在正式環境 use RefreshDatabase; public function testExample() { // 建立5筆資料 factory(Post::class, 5)->create(); // 取得所有資料 $posts = Post::all(); // 斷言結果 $this->assertCount(5, $posts); } } |
接著運行測試,但很重要的記得清除緩存 config:clear,讓測試環境能抓取 .env.testing。
1 2 3 4 5 6 7 8 9 |
php artisan config:clear (Windows) vendor\bin\phpunit (Linux) vendor/bin/phpunit |
功能測試
上面做的是單元測試,那假設我們要測試 API 是否符合預期,我們則要使用功能測試。
新增一個 Controller
1 2 3 |
php artisan make:controller ProductController |
1 2 3 4 5 6 7 8 9 10 11 12 |
class ProductController extends Controller { public function store(Request $request) { return [ 'id' => (int) $request->id, 'title' => 'productTitle' ]; } } |
現在新增路由
1 2 3 4 |
// api.php Route::post('products', 'ProductController@store'); |
接著下指令產生功能測試
1 2 3 |
php artisan make:test ProductTest |
接著嘗試訪問 API,並斷言回傳的 JSON 符合我們的結構
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public function testExample() { $pid = 100; $response = $this->json('POST', 'api/products', [ 'id' => $pid ]); // 斷言結構是否相符合 $response->assertExactJson([ 'id' => $pid, 'title' => 'productTitle' ]); // 斷言回傳值是否相等 $response->assertJson([ 'id' => $pid, 'title' => true ]); } |
1 2 3 4 5 6 7 8 9 |
php artisan config:clear (Windows) vendor\bin\phpunit (Linux) vendor/bin/phpunit |
如果要在 Request 加入 Header 可以這麼寫
1 2 3 4 5 6 7 8 9 10 11 |
$response = $this->withHeaders([ 'X-Header' => 'Value', ])->json('POST', '/user', ['name' => 'Sally']); $response ->assertStatus(201) ->assertJson([ 'created' => true, ]); |
若要為 Response 除錯,可以選用添加
1 2 3 4 |
$response->dumpHeaders(); $response->dump(); |
如果使用 phpStorm 進行測試,我們可以透過設定進行測試,請參考這篇。