基本步驟
隔離測試和生產數據:
總不希望妳生成的假數據存到真的資料庫裡吧。
提供一致的測試環境:
使用測試資料庫,我們可以在每次測試之前重置其狀態,確保測試的一致性。
提高測試速度:
生產數據庫可能包含大量的數據,而測試資料庫可以只包含測試所需的最小數據集。這意味著在測試資料庫上執行測試操作(如資料查詢、插入或更新)通常會比在生產數據庫上更快。也就是說如果我今天只是要測試取出來的資料是否有符合一定格式含有符合預期的狀態碼,我不用一次返回一大包。
my_testing_database
**。在你的Laravel專案中,你可能已經有一個 .env
文件,它包含你的本地開發環境的數據庫設置。為了測試,你可以建立一個 .env.testing
文件,裡面包含測試數據庫的連接信息,例如:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=my_testing_database
DB_USERNAME=root
DB_PASSWORD=your_actual_password
在 phpunit.xml
文件中將這行註解去掉
<env name="DB_DATABASE" value="my_testing_database"/>
執行遷移以建立表結構
使用下列指令執行遷移,這將會在測試數據庫中建立所有必要的表結構:
php artisan migrate --env=testing
選填:執行資料填充器
如果需要填充測試數據,可以運行:
php artisan db:seed --env=testing
編寫和運行測試
現在,當你運行測試時,它應該會使用你為測試設置的數據庫,而不是你的主要開發數據庫。
php artisan make:test NameOfYourTest
這裡我先針對我的寶可夢新增這隻API去寫featureTest(以下是正確的情況)
<?php
namespace Tests\Feature\PokemonStoreTest;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Arr;
use Tests\TestCase;
class HappyPathTest extends TestCase
{
use RefreshDatabase;
/**
* A basic feature test example.
*/
// Happy Path
// 確認傳入正確的參數後:
// 1.是否有正確的狀態馬, 2.是否有存入資料庫
public function testPokemonStore()
{
// 模擬要傳入的參數
$mockedData = createMockData();
// 保存隨機生成的技能ID,以便於後面驗證
$expectedSkills = $mockedData['skills']->pluck('id')->toArray();
// 將模型實例傳入,以便於在函式裡面取得id
$data = getMockData($mockedData);
......後續邏輯
}
}
首先去創建資料,取得資料,分別用到createMockData()和getMockData()。
這裡我加了一行 use RefreshDatabase,目的是為了在每次測試之後都會回滾數據庫的變動,這樣可以確保每次測試的環境都是一致的。
createMockData():
我創建模擬資料的函式(這裡我是寫在輔助函數)
<?php
use App\Models\Ability;
use App\Models\Nature;
use App\Models\Race;
use App\Models\Skill;
function createMockData($evolution_level = 15, $skillNumber = 4)
{
// 創造數據返回id
$race = Race::factory()->create(['evolution_level' => $evolution_level]);
// $user = User::factory()->create(['role' => $role]);
$nature = Nature::factory()->create();
$ability = Ability::factory()->create();
$skills = Skill::factory($skillNumber)->create();
$race->skills()->attach($skills->pluck('id'));
return compact('race', 'nature', 'ability', 'skills');
}
這裡我在參數的部分可以讓使用函數的地方可以選擇是否要更改進化等級及技能,
這部分是為了例外測試用的,比如說我輸入技能數量超過4的話應該要噴錯誤。
getMockData():
前面在於創建資料,拿到的是資料的物件,這裡是從物件裡面取得實際的值後,以陣列返回
<?php
use App\Models\Pokemon;
function getMockData($mockData, $overrides = [])
{
// $mockData = $this->createMockData($role);
$pokemon = Pokemon::factory()->make();
// 接收數據 組裝
$data = [
"name" => $pokemon->name,
"race_id" => $mockData['race']->id,
"skills" => $mockData['skills']->pluck('id')->toArray(),
"ability_id" => $mockData['ability']->id,
"nature_id" => $mockData['nature']->id,
"level" => $mockData['race']->evolution_level - 1,
];
// 用這樣的方法可以彈性的讓使用者輸入參數去修改陣列裡的數值
return array_merge($data, $overrides);
}
在取得資料後就拿這個創建的資料去實際發請球到我的API
確認狀態碼
//發送請求
$response = $this->post('api/pokemons', $data)
->assertStatus(201); // Assuming 201 means created
這laravel 測試class的底層有提供一些發請求的method(像我這裡用的是post),將endpoint打上,還有剛剛創建的資料,然後輸入我的預期的狀態碼,如果實際上返回的有符合預期,就不會噴錯誤。
確認是否新增到資料庫
// 檢查數據庫是否有相對應的數據
$this->assertDatabaseHas('pokemons', Arr::except($data, ['skills']));
// 獲取剛剛創建的 Pokemon
$pokemonStored = \App\Models\Pokemon::where('name', $data['name'])->firstOrFail();
// 檢查存儲的技能是否與預期的技能相匹配
$this->assertEqualsCanonicalizing($expectedSkills, $pokemonStored->skills);
我這裡簡單介紹一下我是如何寫關於正確情況的featureTest,那有關於錯誤情況的,有機會下一篇再分享。