内容简介:本来打算写一篇通过 Laravel Dusk 测试前端 Vue 组件的教程,转念一想不如玩把大的,直接基于 Laravel + Vue 构建一个前后端分离的待办任务列表项目,然后在开发过程中通过 HTTP 功能测试用例测试后端 API 接口,通过浏览器测试用例测试前端 Vue 组件与后端的交互,同时引入数据库测试对增删改查进行测试,从而完成一个简单的、相对完整的测试驱动开发项目。我们创建一个新的 Laravel 应用来完成项目。首先通过如下命令快速初始化一个新的 Laravel 应用,将新项目命名为新项目创
本来打算写一篇通过 Laravel Dusk 测试前端 Vue 组件的教程,转念一想不如玩把大的,直接基于 Laravel + Vue 构建一个前后端分离的待办任务列表项目,然后在开发过程中通过 HTTP 功能测试用例测试后端 API 接口,通过浏览器测试用例测试前端 Vue 组件与后端的交互,同时引入数据库测试对增删改查进行测试,从而完成一个简单的、相对完整的测试驱动开发项目。
构建应用
创建新项目
我们创建一个新的 Laravel 应用来完成项目。首先通过如下命令快速初始化一个新的 Laravel 应用,将新项目命名为 todoapp
:
laravel new todoapp
数据库迁移
新项目创建后,进入 todoapp
目录,修改 .env
中的数据库相关配置以便可以连接到本地开发环境的数据库。然后运行如下 Artisan 命令创建一个新的数据库迁移文件用于创建待办任务表 tasks
:
php artisan make:migration create_tasks_table
该命令会在 database/migrations
目录下生成一个数据库迁移文件 2019_04_16_054738_create_tasks_table.php
,编写该文件代码如下:
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateTasksTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('tasks', function (Blueprint $table) { $table->bigIncrements('id'); $table->text('text'); $table->tinyInteger('is_completed')->unsigned()->default(0); $table->integer('user_id')->unsigned(); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('tasks'); } }
我们在这个数据表中定义三个功能字段, text
用于存放任务名称, is_completed
用于表示任务是否完成, user_id
用于存放对应的用户 ID 以便和用户关联。运行 Artisan 迁移命令在数据库中创建这张数据表:
php artisan migrate
创建模型类
接下来,通过如下 Artisan 命令创建 tasks
对应的模型类:
php artisan make:model Task
该命令会在 app
目录下生成 Task.php
,编写该模型类代码如下:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Task extends Model { const NOT_COMPLETED = 0; const IS_COMPLETED = 1; protected $fillable = ['text', 'is_completed', 'user_id']; public function user() { return $this->belongsTo(User::class); } }
我们在模型类中定义了两个常量属性用于标识任务是否完成,通过 $fillable
属性设置了支持批量赋值的属性,最后定义了一个 user
方法用于表示用户与待办任务之间的一对多关联关系。
定义资源控制器
为了实现对待办任务的增删改查操作,我们为其创建一个资源控制器 TaskController
:
php artisan make:controller TaskController --resource
该命令会在 app/Http/Controllers
目录下生成一个资源控制器 TaskController.php
。在这个控制器中,我们限制只有认证用户才能对任务进行增、删、改操作,未认证游客只能查看任务,由于我们要构建的是前后端分离应用,所以需要通过 auth:api
中间件对未认证用户进行判断,我们暂时先实现其中的 store
、 update
、 delete
三个资源变更方法:
<?php namespace App\Http\Controllers; use App\Task; use Illuminate\Http\Request; class TaskController extends Controller { public function __construct() { $this->middleware('auth:api')->except(['index', 'show']); } /** * Display a listing of the resource. * * @return \Illuminate\Http\Response */ public function index() { // } /** * Show the form for creating a new resource. * * @return \Illuminate\Http\Response */ public function create() { // } /** * Store a newly created resource in storage. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function store(Request $request) { $request->validate([ 'text' => 'required' ]); return Task::create([ 'text' => $request->text, 'user_id' => auth('api')->user()->id, 'is_completed' => Task::NOT_COMPLETED ]); } /** * Display the specified resource. * * @param int $id * @return \Illuminate\Http\Response */ public function show($id) { // } /** * Show the form for editing the specified resource. * * @param int $id * @return \Illuminate\Http\Response */ public function edit($id) { // } /** * Update the specified resource in storage. * * @param Task $task * @return \Illuminate\Http\Response */ public function update(Task $task) { return tap($task)->update(request()->only(['is_completed', 'text']))->fresh(); } /** * Remove the specified resource from storage. * * @param Task $task * @return \Illuminate\Http\Response */ public function destroy(Task $task) { $task->delete(); return response()->json(['message' => 'Task deleted'], 200); } }
在上述代码中,我们用到了 Laravel 框架的控制器中间件、 隐式路由绑定 、 表单字段验证 、辅助函数 tap/auth 以及JSON 响应等功能特性,对这些功能不太熟悉的话,可以点击链接查看相应的文档。
注册路由
定义好控制器后,我们在 API 路由文件 routes/api.php
中定义相应的 API 资源路由指向这个控制器:
Route::resource('task', 'TaskController');
Passport 初始化
由于我们在这个项目中需要用到 API 认证,并且将基于Passport 扩展包实现 API 认证,所以还需要通过 Composer 安装这个扩展包:
composer require laravel/passport
然后运行如下 Artisan 命令初始化 Passport 数据表和认证相关密钥信息:
php artisan migrate php artisan passport:install
最后,不要忘了添加 Laravel\Passport\HasApiTokens
Trait 到 App\User
模型,以便可以在 User
模型上使用 Passport 进行 API 认证:
use Laravel\Passport\HasApiTokens; class User extends Authenticatable { use Notifiable, HasApiTokens; ...
好了,至此,我们就完成了该项目后端功能的初始化工作,下面我们通过编写HTTP 功能测试用例来测试上面定义的三个 API 接口。
编写 HTTP 功能测试用例
编写测试用例 TasksTest
我们还是通过一条 Artisan 命令来生成功能测试用例类:
php artisan make:test TasksTest
该命令会在 tests/Feature
目录下创建一个新的 TasksTest.php
文件,我们在这个文件中定义 HTTP 功能测试代码如下:
<?php namespace Tests\Feature; use Tests\TestCase; use App\Task; use App\User; use Laravel\Passport\Passport; use Illuminate\Foundation\Testing\DatabaseMigrations; use Illuminate\Foundation\Testing\WithFaker; use Illuminate\Foundation\Testing\RefreshDatabase; class TasksTest extends TestCase { use DatabaseMigrations; /** * 测试认证用户可以创建任务 */ public function testUserCanCreateTask() { $user = factory(User::class)->create(); $task = [ 'text' => 'New task text', 'user_id' => $user->id ]; Passport::actingAs($user, ['*']); $response = $this->json('POST', 'api/task', $task); $response->assertStatus(201); $this->assertDatabaseHas('tasks', $task); } /** * 测试访客不能创建任务 */ public function testGuestCantCreateTask() { $task = [ 'text' => 'new text', 'user_id' => 1 ]; $response = $this->json('POST', 'api/task', $task); $response->assertStatus(401); $this->assertDatabaseMissing('tasks', $task); } /** * 测试认证用户可以删除任务 */ public function testUseCanDeleteTask() { $user = factory(User::class)->create(); $task = factory(Task::class)->create([ 'text' => 'task to delete', 'user_id' => $user->id ]); Passport::actingAs($user, ['*']); $response = $this->json('DELETE', "api/task/$task->id"); $response->assertStatus(200); $this->assertDatabaseMissing('tasks', ['id' => $task->id]); } /** * 测试认证用户可以完成任务 */ public function testUserCanCompleteTask() { $user = factory(User::class)->create(); $task = factory(Task::class)->create([ 'text' => 'task to complete', 'user_id' => $user->id ]); Passport::actingAs($user, ['*']); $response = $this->json('PUT', "api/task/$task->id", ['is_completed' => Task::IS_COMPLETED]); $response->assertStatus(200); $this->assertNotNull($task->fresh()->is_completed); } }
在上述代码中,我们编写了四个测试用例,分别用于测试创建任务、删除任务和更新任务接口,并且在创建任务的时候,为了测试未登录游客不能创建任务,还编写了额外的一个测试用例 testGuestCantCreateTask
。
涉及到的相关测试技术
在测试用例中,我们不仅会断言 API 接口的响应状态码,还会断言调用接口后数据库中的对应记录是否存在,以确认更新操作确实生效。有关 HTTP 功能测试和数据库测试的更多断言方法可以参考HTTP 测试和数据库测试文档。
在需要用户认证的场景下,我们使用了Passport 提供的方法模拟用户进行 API 认证。
另外,你可能注意到,我们还在测试类中使用了 DatabaseMigrations
Trait,它的作用是在运行测试用例之前运行 migrate:refresh
命令回滚所有迁移再运行所有迁移,也就是重新构建数据库,然后在应用销毁(测试结束)时运行 migrate:rollback
命令回滚所有已执行的迁移:
<?php namespace Illuminate\Foundation\Testing; use Illuminate\Contracts\Console\Kernel; trait DatabaseMigrations { /** * Define hooks to migrate the database before and after each test. * * @return void */ public function runDatabaseMigrations() { $this->artisan('migrate:fresh'); $this->app[Kernel::class]->setArtisan(null); $this->beforeApplicationDestroyed(function () { $this->artisan('migrate:rollback'); RefreshDatabaseState::$migrated = false; }); } }
编写模型工厂
在这段测试代码中,我们还使用了模型工厂模拟创建数据库记录,由于 User
模型对应的模型工厂 Laravel 框架已经开箱提供,所以我们只需要创建 Task 模型对应的模型工厂即可,我们使用如下 Artisan 命令创建模型工厂:
php artisan make:factory TaskFactory
该命令会在 database/factories
目录下创建 TaskFactory.php
,我们就在这个文件中编写模型工厂:
<?php use Faker\Generator as Faker; $factory->define(\App\Task::class, function (Faker $faker) { return [ 'text' => $faker->text, 'is_completed' => \App\Task::NOT_COMPLETED ]; });
至此,我们的 HTTP 测试用例就编写好了,接下来我们就可以运行这个测试用例来检验代码是否有问题了。
运行 HTTP 测试用例
我们在项目根目下运行如下命令执行测试用例,绿色代表通过:
这就说明我们的后端 API 接口都是 OK 的,如果测试不通过,则需要排查问题,修改代码,直到测试都通过。
下一篇我们将基于 Vue 组件构建前端页面,通过 JavaScript 与后端这些 API 接口交互实现完整的增删改查功能,并通过 Laravel Dusk 编写浏览器测试用例验收整个项目是否合格。
以上所述就是小编给大家介绍的《[ Laravel从入门到精通 ] 测试系列 —— 通过测试驱动开发构建待办任务项目(一):后端 API 接口篇》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- [ Laravel从入门到精通 ] 测试系列 —— 通过测试驱动开发构建待办任务项目(二):前端功能和浏览器...
- 如何使用React Hooks建立一个待办事项列表
- 产品待办事项列表梳理 Product Backlog Grooming
- 禅道 9.8.stable 发布,增强待办功能和消息通知功能
- 喧喧 1.5.0 优化服务器性能,支持将消息创建为然之待办
- 微软待办事项To-Do Windows 10 UWP版更新:支持多账户切换
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
.NET本质论 第1卷:公共语言运行库
博克斯 (BoxDon) / 张晓坤 / 中国电力出版社 / 2004-1 / 48.00元
本书由10章组成,探讨了CLR即公共语言运行库,涵盖了基本类型、实例、方法调用和消息、AppDomain、安全、以及CLR外部世界。一起来看看 《.NET本质论 第1卷:公共语言运行库》 这本书的介绍吧!