測試驅動開發一直被提起,可是該如何實施時候卻又老是一頭霧水,這裏會經過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