快速開始你的第一次單元測試

簡介

Laravel 內建就支援單元測試。事實上,Laravel 預設就支持用 PHPUnit 來做測試,並為你的應用配置好了 phpunit.xml 檔案。框架還提供了一些便利的幫助函數來讓你更直觀的測試你的應用

預設情況下,應用的 tests 目錄中包含兩個子目錄:Feature 和 Unit

單元測試(Unit Tests)是針對你的程式碼中非常少,而且相對獨立的部分來進行的測試。實際上,大部分單元測試都是針對單個方法進行的。單元測試不會啟動你的應用因此無法取用應用資料庫或框架的服務

功能測試(Feature Tests)則是針對大部分的程式碼進行的測試,包括多個物件之間的交互,甚至是對 JSON 端點的完整 HTTP 請求等等。總的來說,你大部分的測試都應該是功能測試,這些測試能讓你確保你的系統能夠正常運作

Feature 和 Unit 目錄中都提供一個 ExampleTest.php 測試範例文件。安裝一個新的 Laravel 應用之後,在命令行下運行 phpunit 或者是 test 命令,即可運行測試

php artisan test

環境

在使用 phpunit 進行測試時,Laravel 將根據 phpunit.xml 文件設定的環境變數自動將環境設置為 testing,並將 Session 及緩存以 array 的驅動形式來保存,也就是說在測試時不會持久保存任何 Session 或緩存資料

你可以隨意創建其它必要的測試環境配置。testing 環境變數可以在 phpunit.xml 文件中修改,但是在運行測試之前,請確保使用以下命令來清除配置的緩存!

php artisan config:clear

.env.testing 檔案

此外,你還可以在你的專案根目錄下創建一個 .env.testing 檔案,在運行單元測試或使用帶有 env=testing 選項的 Artisan 命令時, .env 文件中的變數將會被這個文件覆蓋

快速開始

生成一個測試用例:

使用 Artisan 命令 make:test 創建一個新的測試用例:

// 在 Feature 目錄下創建一個測試類別...
php artisan make:test UserTest

// 在 Unit 目錄下創建一個測試類別...
php artisan make:test UserTest --unit

技巧:

可以使用 stub publishing 自定義測試 stub

測試類別一旦生成,你就可以像使用 PHPUnit 一樣定義測試方法。 執行 phpunit 或者 php artisan test 命令即可執行測試:

//tests\Unit\ExampleTest.php

namespace Tests\Unit;

use PHPUnit\Framework\TestCase; 

class ExampleTest extends TestCase
{
    /**
     * 一個基礎的測試用例
     *
     * @return void
     */
    public function testBasicTest()
    {
        $this->assertTrue(true);
    }
}

注意:

如果要在你的測試類別中定義自己的 setUp / tearDown 方法,請確保呼叫了父類別中的 parent::setUp() / parent::tearDown() 方法。

執行測試

你可以使用 phpunit 執行檔來執行測試如下:

./vendor/bin/phpunit

除 phpunit 命令之外, 您還可以使用 Artisan 的 test 命令來運行你的測試。Artisan 測試運行器提供了關於當前正在運行的測試的更多訊息,以便於日常開發與調試,推薦使用:

php artisan test

此外,任何可以傳遞給 phpunit 命令的參數也可以傳遞給 Artisan 的 test 命令:

php artisan test --testsuite=feature --stop-on-failure

實作展示

Step 1.生成 posts 表格

建立 posts 表格的 Migration 檔案

php artisan make:migration create_posts_table

編輯 Migration 內容如下:

//database\migrations\xxxx_xx_xx_xxxxxx_create_posts_table.php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreatePostsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title', 50);
            $table->longText('content');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('posts');
    }
}

執行 migrate 來生成 posts 表格

php artisan migrate

生成 Post.php 作為 Eloquent 模型

php artisan make:model Post

Step 2.建立假資料工廠

建立 PostFactory.php 檔案,該檔案用來定義生成的欄位資料

php artisan make:factory PostFactory

編輯 PostFactory.php 的內容如下:

//database\factories\PostFactory.php

namespace Database\Factories;

use App\Models\Post;
use Illuminate\Database\Eloquent\Factories\Factory;

class PostFactory extends Factory
{
    /**
     * The name of the factory's corresponding model.
     *
     * @var string
     */
    protected $model = Post::class;

    /**
     * Define the model's default state.
     *
     * @return array
     */
    public function definition()
    {
        return [
            'title' => $this->faker->word,
            'content' => $this->faker->paragraph
        ];
    }
}

Step 3.新增&編輯控制器

新增 PostController.php 用來處理 posts 表格的 CRUD

輸入以下指令來新增空白的 PostController.php

php artisan make:controller PostController

編輯 PostController.php 內容如下:

//app\Http\Controllers\PostController.php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Post;
use Symfony\Component\HttpFoundation\Response;

class PostController extends Controller
{
    public function index(){
        $posts = Post::get();
        return $posts;
    }

    public function show(Request $request,$id){
        $post = Post::findOrFail($id);
        return $post;
    }

    public function store(Request $request){
        $data = ['title' => $request->title , 'content' => $request->content];
        
        return response()->noContent(Response::HTTP_CREATED); //回傳201狀態碼
    }

}

Step 4.撰寫路由

在 routes/web.php 加入對應路由

//routes\web.php

Route::get('/posts','App\Http\Controllers\PostController@index');
Route::post('/posts/store','App\Http\Controllers\PostController@store');
Route::match(['get','post'],'/posts/{id}','App\Http\Controllers\PostController@show');

Step 5.新增測試用例

新增一個功能測試用例

php artisan make:test PostTest

Step 6.編輯測試用例

首先載入 Post 模型 以及 RefreshDatabase ,前者用於控制資料庫,後者則用於重置資料庫

//tests\Feature\PostTest.php

namespace Tests\Feature;

use Tests\TestCase;
use App\Models\Post;
use Illuminate\Foundation\Testing\RefreshDatabase;

class PostTest extends TestCase
{
    use RefreshDatabase; //每次執行時都要重整資料庫

    //測試是否正確的建立5筆資料
    public function test_posts_count()
    {
        Post::factory()->count(5)->create(); //生成五筆假資料

        $posts = Post::get();

        $this->assertCount(5,$posts); //確認資料筆數
    }

    //測試 /posts 路徑能否正常訪問
    public function test_index_get()
    {
        Post::factory()->count(5)->create(); //生成五筆假資料
        $response = $this->get('/posts');

        $response->assertStatus(200); //確認狀態碼
    }

    //測試 /posts 路徑能否看到指定的標題
    public function test_index_see()
    {
        Post::factory()->count(5)->create(); //生成五筆假資料
        $response = $this->get('/posts');

        $post = Post::first();
        $response->assertSee($post->title);
    }

    //測試 /posts/{id} 路徑能否正常用get訪問
    public function test_show_get()
    {
        Post::factory()->count(5)->create(); //生成五筆假資料
        $post = Post::first();

        $response = $this->get("/posts/{$post->id}");
        $response->assertStatus(200);
    }

    //測試 /posts/{id} 路徑能否正常用post訪問
    public function test_show_post()
    {
        Post::factory()->count(5)->create(); //生成五筆假資料
        $post = Post::first();

        $response = $this->post("/posts/{$post->id}");
        $response->assertStatus(200);
    }

    //測試 /posts/store 路徑能否正常用來新增資料
    public function test_store_post()
    {
        $post = Post::factory()->make();
        $response = $this->post('/posts/store',['title'=>$post['title'],'content'=>$post['content'] ]);
        $response->assertStatus(201);
    }
}

Step 7.執行測試

執行所有測試用例的命令

php artisan test

如果測試成功會顯示綠色的打勾,反之失敗則會顯示紅色,並且說明錯誤的程式碼

進階技巧

並行測試

預設情況下,Laravel 和 PHPUnit 會在單一線程依序地執行每個測試。假如你覺得測試所需要花費的時間過久就可以考慮以多線程並行的方式來大幅縮減測試所需的時間。要進行並行測試,只需要在 test 命令後面加上 parallel 選項即可

php artisan test --parallel

啟動並行時,預設 Laravel 將會盡可能的使用機器上所有的 CPU,但是你還是可以去限制所要給它使用的數量,透過 processes 選項

php artisan test --parallel --processes=4

Select a repo