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 選項的 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
建立 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
建立 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
];
}
}
新增 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狀態碼
}
}
在 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');
新增一個功能測試用例
php artisan make:test PostTest
首先載入 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);
}
}
執行所有測試用例的命令
php artisan test
如果測試成功會顯示綠色的打勾,反之失敗則會顯示紅色,並且說明錯誤的程式碼
預設情況下,Laravel 和 PHPUnit 會在單一線程依序地執行每個測試。假如你覺得測試所需要花費的時間過久就可以考慮以多線程並行的方式來大幅縮減測試所需的時間。要進行並行測試,只需要在 test 命令後面加上 –parallel 選項即可
php artisan test --parallel
啟動並行時,預設 Laravel 將會盡可能的使用機器上所有的 CPU,但是你還是可以去限制所要給它使用的數量,透過 –processes 選項
php artisan test --parallel --processes=4