使用 Laravel 进行商品功能测试

在查看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,最终达到我们测试的预期

使用 Laravel 进行功能测试

删除商品测试

同样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 协议》,转载必须注明作者和本文链接
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!