在檢視Laravel的HTTP測試,在開發過程中,進行相關的功能測試是有必要的。
以下是我進行的相關api功能測試:
通過命令php artisan make:test GoodsTest
建立一個功能測試類
建立的預設頁面如下:
class GoodsTest extends TestCase
{
/**
* A basic feature test example.
*
* @return void
*/
public function testExample()
{
$response = $this->get('/');
$response->assertStatus(200);
}
}
在呼叫相關介面時,首先需要進行使用者認證,首先我需要獲取使用者登入token,並在請求api介面時,在請求頭部新增token,以此完成使用者認證。
定義登入路由
Route::group(['namespace' => 'Auth'], function () {
//提交登入資訊
Route::any('/login', 'LoginController@login')->name('admin.login');
});
登入介面程式碼如下:
public function login(Request $request)
{
$info = $request->input('info');
$token = Auth::guard('admin')->attempt(['email'=>$info['email'],'password'=>$info['password']],true);
$admin = Auth::guard('admin')->user();
if(!$admin){
return $this->failed('賬號密碼錯誤,請重新輸入');
}
return $this->success($token);
}
以上程式碼為判斷使用者賬號密碼是否正確,若正確返回token,響應狀態為200;否則響應狀態為400;
編寫新增模擬登入測試方法
/**
* @test
* @return void
* 模擬使用者登入
*/
public function login()
{
$data = [
'email' => 'test@qq.com',
'password' => '123456'
];
$response = $this->json('GET',route('admin.login'),['info'=>$data]);
$response->assertStatus(200);
$token = $response->json();
return $token;
}
這裡以電商中的商品為例,對商品的建立,刪除進行相關的功能測試。
商品建立測試
在以下例子中,首先通過獲取Faker例項,Faker可以方便我們建立測試資料,詳細使用可檢視faker資料填充詳解
建立商品的測試方法依賴於login方法,通過接受login方法傳遞的token,在請求建立商品介面中,傳遞token完成使用者認證,並傳遞商品資料和商品屬性資料。後驗證介面狀態是否正確,資料庫是否存在相關商品資料和商品屬性資料。
/**
* 新增商品資料
* @test
* @depends login
* @param $token
*/
public function create_goods($token)
{
// 獲取 Faker 例項
$faker = \Faker\Factory::create();
//商品資料(商品名稱,標題,描述,價格,商品詳情,商品圖片,屬性總攬陣列)
$info = [
'name' => $faker->sentence(5,true),
'title' => $faker->sentence(5,true),
's_desc' => $faker->text(),
'position' => $faker->numberBetween(1,100),
'good_grade' => $faker->numberBetween(1,5),
'weight' => $faker->numberBetween(100,1000),
'old_price' => $faker->randomFloat(2, 10, 15),
'sale_price' => $faker->randomFloat(2, 5, 10),
'content' => $faker->randomHtml(),
'mask' => $faker->imageUrl(),
'attr_list' => [
"a" => [
"index" => "a"
"name" => "color"
"list" => array:2 [
"k0" => "red"
"k1" => "yellow"
]
]
"b" => [
"index" => "b"
"name" => "size"
"list" => array:3 [
"k0" => "big"
"k1" => "small"
"k2" => "middle"
]
]
];
//屬性預期數量
$attrCount = collect($info['attr_list'])->map(function ($attr){
return count($attr['list']);
})->reduce(function ($count,$item){
return $count*$item ? $count*$item : $item;
});
//商品屬性資料(包括商品屬性名,屬性標識,舊價格,現價,商品sku,庫存等)
$goodsAttr = [
[
'name'=>'red/big',
'attr'=>"[\"ak0\",\"bk0\"]",
'sku'=>$faker->uuid,
'old_price'=>$faker->randomFloat(2, 10, 15),
'price'=>$faker->randomFloat(2, 10, 15),
'count'=>$faker->numberBetween(1, 100),
'weight'=>$faker->numberBetween(1, 100)
],
[
'name'=>'red/small',
'attr'=>"[\"ak0\",\"bk1\"]",
'sku'=>$faker->uuid,
'old_price'=>$faker->randomFloat(2, 10, 15),
'price'=>$faker->randomFloat(2, 10, 15),
'count'=>$faker->numberBetween(1, 100),
'weight'=>$faker->numberBetween(1, 100)
],
[
'name'=>'red/middle',
'attr'=>"[\"ak0\",\"bk2\"]",
'sku'=>$faker->uuid,
'old_price'=>$faker->randomFloat(2, 10, 15),
'price'=>$faker->randomFloat(2, 10, 15),
'count'=>$faker->numberBetween(1, 100),
'weight'=>$faker->numberBetween(1, 100)
],
[
'name'=>'yellow/big',
'attr'=>"[\"ak1\",\"bk0\"]",
'sku'=>$faker->uuid,
'old_price'=>$faker->randomFloat(2, 10, 15),
'price'=>$faker->randomFloat(2, 10, 15),
'count'=>$faker->numberBetween(1, 100),
'weight'=>$faker->numberBetween(1, 100)
],
[
'name'=>'yellow/small',
'attr'=>"[\"ak1\",\"bk1\"]",
'sku'=>$faker->uuid,
'old_price'=>$faker->randomFloat(2, 10, 15),
'price'=>$faker->randomFloat(2, 10, 15),
'count'=>$faker->numberBetween(1, 100),
'weight'=>$faker->numberBetween(1, 100)
],
[
'name'=>'yellow/middle',
'attr'=>"[\"ak1\",\"bk2\"]",
'sku'=>$faker->uuid,
'old_price'=>$faker->randomFloat(2, 10, 15),
'price'=>$faker->randomFloat(2, 10, 15),
'count'=>$faker->numberBetween(1, 100),
'weight'=>$faker->numberBetween(1, 100)
],
];
$response = $this->withHeader('Authorization',$token)
->json('POST','/api/goods',['info'=>$info,'goodsAttrTable'=>$goodsAttr,'attr_list'=>$info['attr_list']]);
$response->assertStatus(200);
$data = $response->json();
//若響應為200返回商品id
$goodsId = $data;
//判斷商品是否存在資料庫
$this->assertDatabaseHas('goods',['id'=>$goodsId]);
//判斷資料庫屬性數量
$attr = GoodsAttr::where(['goods_id'=>$goodsId])->get()->toArray();
$this->assertEquals(count($attr),$attrCount);
}
在編寫測試用例的過程中,一開始的傳入資料和介面邏輯方面會存在相應問題,我可以通過編寫這種測試用例的方式,不斷完善測試用例和介面程式碼邏輯,以此完善程式碼的容錯性和健壯性。這種開發方式也叫TDD(測試驅動開發).
強烈推薦閱讀以下兩篇文章:
重新認識 PHPUnit —— 從這裡開始學習 PHP 下的 TDD(測試驅動開發)開發方法
使用 TDD 測試驅動開發來構建 Laravel REST API
最終在一步步的修改測試用例和介面程式碼後,通過不斷重複執行phpunit --filter=GoodsTest
,最終達到我們測試的預期
刪除商品測試
同樣delete_goods測試方法依賴於login方法,我還對上個例子的create_goods方法做了調整,將建立的商品id返回,做為delete_goods的依賴,並對刪除介面返回狀態碼進行斷言,資料庫軟刪除斷言,獲取商品詳情介面進行狀態碼斷言測試。
/**
* 刪除商品
* @test
* @depends login
* @depends create_goods
* @param $token
* @param $id
*/
public function delete_goods($token,$id)
{
//商品id
$goodsId = $id;
$response = $this->withHeader('Authorization',$token)
->json('DELETE','/api/goods/'.$goodsId);
//驗證狀態碼
$response->assertStatus(200);
//是否已被軟刪除
$this->assertSoftDeleted('goods',['id'=>$goodsId]);
//獲取商品詳情資訊介面
$responseEdit = $this->withHeader('Authorization',$token)
->json('GET','/api/goods/'.$goodsId.'/edit');
$responseEdit->assertStatus(404);
}
使用模型工廠進行批量資料測試
通過命令php artisan make:factory GoodsFactory --model=Goods
生成商品的資料工廠
商品的基本資料可通過faker例項建立隨機測試資料,但由於商品的屬性總攬資料相對複雜,我通過自定義函式createAttr()建立生成相應資料。
//定義商品基本資料
$factory->define(Goods::class, function (Faker $faker) {
return [
'name' => $faker->sentence(5,true),
'title' => $faker->sentence(5,true),
's_desc' => $faker->text(),
'old_price' => $faker->randomFloat(2, 10, 15),
'sale_price' => $faker->randomFloat(2, 5, 10),
'content' => $faker->randomHtml(),
'mask' => $faker->imageUrl(),
'attr_list' => createAttr()
];
});
function createAttr()
{
//生成屬性總攬
}
定義資料供給器
定義goods_data方法為資料供給器,在create_goods方法中使用資料供給器進行批量測試
/**
* 生成商品資料
*/
public function goods_data()
{
$goodsData = factory(Goods::class,10)->make()->toArray();
return $goodsData;
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結