基于 TDD 模式在 Laravel 项目中开发后端评论接口

在继续编写前端 Vue 评论组件之前,我们使用同样的测试驱动开发模式在 Laravel 项目中开发后端评论接口,正如学院君前面说的,除了框架和语法的不同,思路和模式是一模一样的。
编写测试用例
首先,在 Laravel 项目根目录下运行 make:test 命令创建一个测试类 CommentTest:

然后打开这个测试类文件(位于 tests/Feature 目录下),编写简单的符合 BDD 风格的评论保存和获取接口测试用例代码如下:
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
class CommentTest extends TestCase
{
    use RefreshDatabase;  // 测试用例在执行完毕后回滚数据库更新
    use WithFaker;  // 引入 Faker 伪造评论内容
    protected function setUp(): void
    {
        parent::setUp();
        $this->faker = $this->makeFaker('zh_CN');  // 本地化 Faker
    }
    /**
     * @test
     */
    public function submit_comment_add_then_display()
    {
          // Given
        $content = $this->faker->sentence;
        // When
        $response = $this->post('/comments', ['content' => $content]);
        // Then
        $response->assertStatus(200);
        $response = $this->get('/comments');
        $response->assertSeeText($content);
    }
}
我们先通过 Faker 伪造评论内容,调用后端保存评论接口,保存成功后,再调用后端评论列表接口,如果返回数据包含刚刚提交的评论,则意味着评论保存和获取功能可用。
现在通过 php artisan test tests/Feature/CommentTest.php 命令运行这个测试用例,肯定会失败,因为后端接口都没有实现:

所有相关的响应断言返回的都是 404 状态码。接下来我们需要编写业务代码。
路由和控制器
首先我们需要注册相应的后端接口路由:
use App\Http\Controllers\CommentController;
Route::group(['prefix' => '/comments'], function () {
    Route::get('/', [CommentController::class, 'all']);
    Route::post('/', [CommentController::class, 'store']);
});
以及对应的控制器代码:
<?php
namespace App\Http\Controllers;
use App\Models\Comment;
use Illuminate\Http\Request;
class CommentController extends Controller
{
    public function all()
    {
        return Comment::orderByDesc('created_at')->simplePaginate(10);
    }
    public function store(Request $request)
    {
        $data = $request->validate([
            'content' => 'required'
        ]);
        $comment = new Comment($data);
        return $comment->save();
    }
}
此时测试仍然不通过,因为模型类和数据库还不存在:

所以响应状态码是 500。
模型类和数据库准备
接下来我们创建模型类和数据库迁移文件:
php artisan make:model Comment -m
生成的模型类位于 app/Models/Comment.php,我们在这个模型类中设置批量赋值白名单和关联关系:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
    use HasFactory;
    protected $fillable = ['content'];
    public function user()
    {
        $this->belongsTo(User::class);
    }
}
在 .env 中配置数据库连接信息(需要提前创建好数据库 component_test):
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=component_test
DB_USERNAME=root
DB_PASSWORD=root
编辑评论表对应的数据库迁移文件如下:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateCommentsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('comments', function (Blueprint $table) {
            $table->id();
            $table->bigInteger('user_id')->unsigned()->default(0);
            $table->string('content');
            $table->timestamps();
        });
    }
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('comments');
    }
}
我们只设置了主键 ID、user_id、content 以及创建/更新时间几个字段。然后运行 php artisan migrate 命令创建数据表:

让业务代码通过测试用例
此时再去运行 php artisan test 测试命令,测试通过:

表明我们的后端评论接口已经可以正常对外提供服务。
怎么样,你已经熟悉了 TDD 开发模式的精髓了吧:基于业务需求编写 BDD 风格的测试用例,然后编写能够通过这个测试用例的业务代码,接下来编写下一个测试用例。。。最终交付完整可用的功能代码。相信你会和我一样逐渐爱上这种让测试用例从红色变成绿色的感觉!
这里可以将业务代码编写和单元测试运行粒度变得更小,比如注册路由、编写控制器动作、实现模型类、数据表初始化等每个步骤进行一次测试,逐步迭代出通过测试用例的代码。测试驱动开发有两种模式,一种是先写测试用例,再写业务代码,一种是先写业务代码,再写测试用例,最终都要让业务代码通过每个测试用例,既然最终结果变绿,看你个人更喜欢哪种模式了。更多关于 Laravel 测试的功能和使用细节,请参考 Laravel 官方文档。
下篇教程,学院君将给大家演示如何在前端 Vue 评论组件调用后端接口保存评论信息和渲染评论列表。
本系列教程首发在Laravel学院(laravelacademy.org),你可以点击页面左下角阅读原文链接查看最新更新的教程。
