测试驱动开发一直被提起,可是该如何实施时候却又老是一头雾水,这里会经过laravel 5.6实现一个简单注册功能来说下如何实施测试驱动开发php
这是测试驱动开发的第一步,首先须要对需求有必定的理解,这关系到后面测试用例的编写mysql
咱们要实现的是一个注册功能,须要用户填入username和password字段,同时还须要验证这两个字段的合法性laravel
首先打开 config/database.php 中加入sql
<?php return [ 'default' => env('DB_CONNECTION', 'mysql'), 'connections' => [ // 测试开发用的数据库配置 'mysql_test' => [ 'driver' => 'mysql', 'host' => env('DB_HOST', '127.0.0.1'), 'port' => env('DB_PORT', '3306'), 'database' => env('DB_DATABASE', 'forge'), 'username' => env('DB_USERNAME', 'forge'), 'password' => env('DB_PASSWORD', ''), ], ], ];
而后打开 phpunit.xml 加入shell
<env name="DB_CONNECTION" value="mysql_test"/>
它的做用是用来添加或替换掉 DB_CONNECTION 环境变量,当跑测试用例的时候使用 mysql_test 这个数据库链接,环境变量分为下面两个数据库
这里只是实现个注册功能,因此只须要一个表api
php artisan make:migrate create_user_table
实现工具
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateUserTable extends Migration { /** * Run the migrations. */ public function up() { if (!Schema::hasTable('user')) { Schema::create('user', function (Blueprint $table) { $table->engine = 'InnoDB'; $table->increments('id'); $table->string('username', 20)->default('')->coment('用户名'); $table->string('password', 32)->default('')->comment('密码'); $table->timestamps(); $table->index('username'); }); } } /** * Reverse the migrations. */ public function down() { Schema::dropIfExists('user'); } }
生成数据表post
php artisan migrate
php artisan make:model User
指明model用的数据表测试
<?php namespace App; use Illuminate\Database\Eloquent\Model; class User extends Model { protected $table = 'user'; }
模型工厂的做用在于帮助咱们测试的时候快速生成假数据
php artisan make:factory UserFactory
打开UserFactory编辑假数据生成的规则
<?php use Faker\Generator as Faker; $factory->define(App\User::class, function (Faker $faker) { return [ 'username' => $faker->unique()->userName, 'password' => md5('qwerty123456'), ]; });
php artisan make:controller UserController
配置路由
Route::post('/register', 'UserController@register')->name('register');
终于到编写测试用例的时候了,首先建立咱们的测试文件
// 建立Feature测试 php artisan make:test UserTest // 建立Unti测试 php artisan make:test UserTest --unit
编辑测试用例
<?php namespace Tests\Feature; use Tests\TestCase; use Illuminate\Foundation\Testing\RefreshDatabase; use App\User; class UserTest extends TestCase { use RefreshDatabase; private $prefix = '/api/v1'; // username为空 public function test_register_username_is_empty() { $this->post("{$this->prefix}/register", [ 'username' => '', 'password' => '111111', ]) ->assertStatus(422) ->assertSee('username is not empty'); } // username长度大于20 public function test_register_username_length_gt_20() { $this->post("{$this->prefix}/register", [ 'username' => 'aaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'password' => '111111', ]) ->assertStatus(422) ->assertSee('username\'s length can\'t great than 20'); } // username不合法 public function test_register_username_is_invalid_payload_1() { $this->post("{$this->prefix}/register", [ 'username' => '132asdf', 'password' => '111111', ]) ->assertStatus(422) ->assertSee('username is invalid'); } // username不合法 public function test_register_username_is_invalid_payload_2() { $this->post("{$this->prefix}/register", [ 'username' => 'asdfas%sdfsaf', 'password' => '111111', ]) ->assertStatus(422) ->assertSee('username is invalid'); } // username已存在 public function test_register_username_exists() { // 往数据库建立一个用户 $user = factory(User::class)->create(); $this->post("{$this->prefix}/register", [ 'username' => $user->username, 'password' => '111111', ]) ->assertStatus(422) ->assertSee('username is exists'); } // password为空 public function test_register_password_is_empty() { $this->post("{$this->prefix}/register", [ 'username' => 'helbing', 'password' => '', ]) ->assertStatus(422) ->assertSee('password is not empty'); } // password长度大于20 public function test_register_password_length_gt_20() { $this->post("{$this->prefix}/register", [ 'username' => 'helbing', 'password' => 'aaaaaaaaaaaaaaaaaaaaaaaaaaaa', ]) ->assertStatus(422) ->assertSee('password\'s length can\'t great than 20'); } // 注册成功 public function test_register_success() { $this->post("{$this->prefix}/register", [ 'username' => 'helbing', 'password' => 'helbing', ])->assertStatus(201); } }
其实你会发现上面的每一条测试用例都是咱们在开发中必须进行自测的项
编写完测试用例就能够开始实现功能了。既然是要实施测试驱动开发,那么咱们就要利用上咱们已经写好的测试用例。在项目根目录执行命令
vendor/bin/phpunit --filter UserTest::test_register_username_is_empty
咱们经过命令跑了下UserTest测试文件里的test_register_username_is_empty用例,很显然确定是经过不了,这就须要咱们实现接口的功能来让测试用例能经过了
看到这里你们应该明白什么是测试驱动开发了吧,在开发时编写一个功能的测试用例,而后一条一条的实现测试用例,如此往复下去,直到实现全部功能
在测试驱动开发中该如何进行debug呢?在测试驱动开发中,还用echo和var_dump进行debug基本是行不通的,这里建议经过xdebug来进行debug
具体如何作?其实很简单
固然echo和var_dump仍是很好用的,在一些很差使用xdebug的地方也能够经过echo和var_dump来debug,这些都是工具,就看你怎么用它们了
咱们经过实现一个简单注册功能来说解什么是测试驱动开发,可是在实际的实施测试驱动开发仍是会有不少坑要踩的
假设咱们开发的接口使用jwt来作验证,那么咱们的测试用例能够这么写
public function test_create_article() { // 重置环境,保证每次跑实例的时候环境都是新的 $this->refreshApplication(); $this->refreshDatabase(); // 生成一个用户 $token = $this->createUserAndLogin(); // 使用数据工厂模生成假数据 $data = factory(Article::class)->make(); $this->post("{$this->prefix}/article", $data, ['HTTP_Authorization' => "Bearer {$token}"]) ->assertStatus(201); } private function createUserAndLogin() { $user = factory(User::class)->create(); if ($token = JWTAuth::fromUser($user)) { return $token; } return ''; }
这个能够参考官方文档 Mocking