Laravel 测试教程
迁移文件
修改 database/migrations/2014_10_12_000000_create_users_table.php
。
class CreateUsersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
// following table is storing the relationship between users
// user_id is following follow_user_id
Schema::create('following', function (Blueprint $table) {
$table->integer('user_id')->unsigned()->index();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->integer('follow_user_id')->unsigned()->index();
$table->foreign('follow_user_id')->references('id')->on('users')->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('following');
Schema::dropIfExists('users');
}
}
执行迁移
php artisan migrate
Migration table created successfully.
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table
User Model
修改 app/User.php
class User extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password', 'remember_token',
];
public function follows(User $user)
{
$this->following()->attach($user->id);
}
public function unfollows(User $user)
{
$this->following()->detach($user->id);
}
public function following()
{
return $this->belongsToMany('App\User', 'following', 'user_id', 'follow_user_id')->withTimestamps();
}
public function isFollowing(User $user)
{
return !is_null($this->following()->where('follow_user_id', $user->id)->first());
}
}
这里使用了多对多用户关系关联方法[1]。
种子文件
创建一个种子文件 database/seeds/UsersTableSeeder.php
php artisan make:seeder UsersTableSeeder
在种子文件中使用工厂方法[2]
use App\User;
use Illuminate\Database\Seeder;
class UsersTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$users = factory(User::class, 10)->create();
}
}
执行种子文件
- 方式一
php artisan db:seed --class=UsersTableSeeder
- 方式二
在 database/seeds/DatabaseSeeder.php
中注册种子文件
class DatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$this->call(UsersTableSeeder::class);
}
}
执行这个 DatabaseSeeder
这个大种子。
php artisan db:seed
测试用例
创建一个测试用例。
php artisan make:test Feature\UserTest
修改 tests/Feature/UserTest.php
的内容。
use App\User;
class UserTest extends TestCase
{
public function test_have_10_users()
{
$this->assertEquals(10, User::count());
}
}
执行测试
vendor\bin\phpunit
完整的测试文件
<?php
use App\User;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class UserTest extends TestCase
{
use DatabaseTransactions;
public function test_follows()
{
$userA = User::find(2);
$userB = User::find(3);
$userA->follows($userB);
$this->assertEquals(1, $userA->following()->count());
}
public function test_unfollows()
{
$userA = User::find(3);
$userB = User::find(2);
$userA->unfollows($userB);
$this->assertEquals(0, $userA->following()->count());
}
public function test_A_follows_B_and_C()
{
$userA = User::find(1);
$ids = collect([2, 3, 4, 5, 6, 7, 8, 9, 10]);
$random_ids = $ids->random(2);
$userB = User::find($random_ids->pop());
$userC = User::find($random_ids->pop());
$userA->follows($userB);
$userA->follows($userC);
$this->assertEquals(2, $userA->following()->count());
}
}
每执行一次测试,都会在数据库表中插入数据。因此这一次运行结果和前一次可能不一样。
我们不得不像下面这样,每执行一次测试,就得重新 refresh & seeding吗?
php artisan migrate:refresh --seed
vendor\bin\phpunit
不用!加上 DatabaseTransactions
就好了。
class UserTest extends TestCase
{
use DatabaseTransactions;
...
}
这个 Trait 将所有测试项放在一个事务中。无论执行结果如何(全部成功或者没全部成功)都不会对之前的数据库数据造成任何影响,也就是说这个事务在最后总是回滚。