跳至正文

TDD测试驱动开发笔记

教程

learnku社区 Laravel TDD

TDD 构建 Laravel 论坛笔记

Testing Laravel 单元测试入门笔记

总结

在使用 TDD 进行开发时,请牢记以下三项法则:

  1. 在编写失败的测试之前,不要编写任何业务代码;
  2. 只要有一个单元测试失败了,就不要再写测试代码。无法通过编译也是一种失败情况;
  3. 业务代码恰好能够让当前失败的测试成功通过即可,不要多写;

要掌握PHPUnit的用法,如何测试方法,如何测试类,等等…

根据需求结果逆向分析开发的方式。先假设已经实现了需求的方法,然后一步步倒退,缺少什么就创建什么。

需求结果 => 结果方法 => 按需(缺少创建)代码的元素

根据需求准备好测试用例数据

非常适合复杂逻辑需求的推理分析。对于简单的一下就能想出方案的需求,处于节省时间的考虑,可以不用。

测试覆盖率不一定要100%,个人想法,测试要首先覆盖核心复杂迭代频率较高主要业务,简单的业务可以跳过。

和普通开发方式一样,首先要对项目的整体需求和完整逻辑有详细了解,要有基本的解决方案思路

使用渐进式的编码思路,在mvc框架做业务开发时,可以先在控制器方法写出完整的逻辑,即使代码非常多,然后再根据需求迭代使用设计模式或面向对象设计方法,按需要拆分封装成类或方法,完成由面向过程=>面向对象的演变

项目案例说明

要实现一个产品型号搜索候选项功能

需要给前端提供一个http api ,并且要满足各种情景的搜索条件,如图

1.假设这个接口已经存在了,然后创建一个商品接口测试类,写好接口测试方法和各种情境下的测试用例数据。

namespace Tests\Feature;
class GoodsPcApiTest extends TestCase
{ 
/**
     * A basic feature test example.
     *
     * @return void
     */
    public function test_goods_search_option()
    {
        //只有分隔符
        $response = $this->post('/api/goods/search-option', [
            'keywords'=>'-',
        ]);
       //使用dump打印结果是为了方便调试,调试完毕可以注释掉
        dump('只有分隔符', $response->json());
        $response->assertStatus(200);

        //只有产品型号
        $response = $this->post('/api/goods/search-option', [
            'keywords'=>'AC013',
        ]);
        dump('只有产品型号', $response->json());
        $response->assertStatus(200);

        //产品型号+1分隔符
        $response = $this->post('/api/goods/search-option', [
            'keywords'=>'AC013-',
        ]);
        dump('产品型号+1分隔符', $response->json());
        $response->assertStatus(200);

        //产品型号+1分隔符和参数
        $response = $this->post('/api/goods/search-option', [
            'keywords'=>'AC013-d',
        ]);
        dump('产品型号+1分隔符和1参数', $response->json());
        $response->assertStatus(200);
       ...
    }
...
}

然后运行测试,此时测试必然报错,就需要去创建这个接口的控制器和方法以及路由

//路由文件routes/api.php
Route::prefix('goods')->group(function () {
...
    Route::post('search-option', [GoodsController::class, 'searchOption']); //搜索候选项
...
});
//控制器
namespace App\Http\Controllers\Api;
class GoodsController extends Controller
{
  ...
  public function searchOption(Request $request)
    {   
        //验证参数
        $request->validate([
            'keywords'=>'bail|required|string',
        ]);
        //需要根据分隔符-拆分数据
        $keywords = $request->input('keywords');
        $attribute = array_filter(explode($sep, $keywords));
        //要从模型查询,直接调用,如果测试时报错模型不存在就去创建模型
        //如果运行测试报错数据表不存在,就创建数据迁移
        GoodsParamsSku::select()->where()->get();
        //需要一个方法或类来实现某个功能,先调用,然后运行测试,报错functionName方法不存在
        $this->functionName()
 
    }
  ...
}

根据报错创建上述functionName的单元测试

namespace Tests\Unit;

use PHPUnit\Framework\TestCase;

class xxxxFunctionTest extends TestCase
{
    /**
     * A basic unit test example.
     *
     * @return void
     */
    public function test_function_name()
    {
        $this->assertTrue(true);
    }
}