數(shù)據(jù)庫(kù)測(cè)試
數(shù)據(jù)庫(kù)測(cè)試
Database Testing
簡(jiǎn)介
Laravel 提供了各種有用的工具,可以更輕松地測(cè)試數(shù)據(jù)庫(kù)驅(qū)動(dòng)的應(yīng)用程序。 首先,您可以使用 assertDatabaseHas
幫助程序斷言數(shù)據(jù)庫(kù)中存在的數(shù)據(jù)與給定的一組條件匹配。 例如,如果您要驗(yàn)證 users
表中的記錄是否具有 sally @ example.com
的 email
值,您可以執(zhí)行以下操作:
public function testDatabase(){ // Make call to application... $this->assertDatabaseHas('users', [ 'email' => 'sally@example.com' ]); }
您還可以使用 assertDatabaseMissing
幫助程序斷言數(shù)據(jù)庫(kù)中不存在數(shù)據(jù)。
assertDatabaseHas
方法和其他類似的幫助程序是為了方便起見(jiàn)。 您可以自由使用任何 PHPUnit 的內(nèi)置斷言方法來(lái)補(bǔ)充您的測(cè)試。
生成模型工廠
使用 make:factory
Artisan command 命令可以創(chuàng)建一個(gè)模型工廠:
php artisan make:factory PostFactory
新生成的工廠位置在 database/factories
目錄下。
--model
選項(xiàng)可用于指示工廠創(chuàng)建的模型的名稱。 此選項(xiàng)將使用給定模型預(yù)填充生成的工廠文件:
php artisan make:factory PostFactory --model=Post
每次測(cè)試后重置數(shù)據(jù)庫(kù)
在每次測(cè)試后重置數(shù)據(jù)庫(kù)通常很有用,這樣前一次測(cè)試的數(shù)據(jù)不會(huì)干擾后續(xù)測(cè)試。 RefreshDatabase
特征采用最優(yōu)化的方法來(lái)遷移測(cè)試數(shù)據(jù)庫(kù),具體取決于您使用的是內(nèi)存數(shù)據(jù)庫(kù)還是傳統(tǒng)數(shù)據(jù)庫(kù)。 在測(cè)試類上使用特征,一切都將為您處理:
<?php namespace Tests\Feature; use Tests\TestCase; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\WithoutMiddleware; class ExampleTest extends TestCase{ use RefreshDatabase; /** * 一個(gè)基本功能測(cè)試示例 * * @return void */ public function testBasicExample() { $response = $this->get('/'); // ... } }
創(chuàng)建模型工廠
測(cè)試時(shí),您可能需要在執(zhí)行測(cè)試之前將幾條記錄插入數(shù)據(jù)庫(kù)。 在創(chuàng)建此測(cè)試數(shù)據(jù)時(shí), Laravel 不是手動(dòng)指定每列的值,而是允許您使用模型工廠為每個(gè) Eloquent 模型 定義一組默認(rèn)屬性。 開(kāi)始測(cè)試前,請(qǐng)查看應(yīng)用程序中的 database / factories / UserFactory.php
文件。 開(kāi)箱即用,此文件包含一個(gè)工廠定義:
use Illuminate\Support\Str; use Faker\Generator as Faker; $factory->define(App\User::class, function (Faker $faker) { return [ 'name' => $faker->name, 'email' => $faker->unique()->safeEmail, 'email_verified_at' => now(), 'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret 'remember_token' => Str::random(10), ]; });
在用作工廠定義的 Closure 中,您可以返回模型上所有屬性的默認(rèn)測(cè)試值。 Closure 將收到 Faker PHP 庫(kù)的一個(gè)實(shí)例,它允許您方便地生成各種隨機(jī)數(shù)據(jù)以進(jìn)行測(cè)試。
您還可以為每個(gè)模型創(chuàng)建其他工廠文件,以便更好地進(jìn)行組織。 例如,您可以在 database / factories
目錄中創(chuàng)建 UserFactory.php
和 CommentFactory.php
文件。 factories
目錄中的所有文件都將由 Laravel 自動(dòng)加載。
您可以通過(guò)在
config / app.php
配置文件中添加faker_locale
選項(xiàng)來(lái)設(shè)置 Faker 的語(yǔ)言環(huán)境。
工廠狀態(tài)
States 允許您定義可以任意組合應(yīng)用于模型工廠的離散修改。 例如,您的 User
模型可能具有 deinquent
狀態(tài),可以修改其默認(rèn)屬性值之一。 您可以使用 state
方法定義狀態(tài)轉(zhuǎn)換。 對(duì)于簡(jiǎn)單狀態(tài),您可以傳遞一組屬性修改:
$factory->state(App\User::class, 'delinquent', [ 'account_status' => 'delinquent', ]);
如果你的狀態(tài)需要計(jì)算或 $ faker
實(shí)例,你可以使用 Closure 來(lái)計(jì)算狀態(tài)的屬性修改:
$factory->state(App\User::class, 'address', function ($faker) { return [ 'address' => $faker->address, ]; });
工廠回調(diào)
使用 afterMaking
和 afterCreating
方法注冊(cè)工廠回調(diào),并允許您在創(chuàng)建或創(chuàng)建模型后執(zhí)行其他任務(wù)。 例如,您可以使用回調(diào)將其他模型與創(chuàng)建的模型相關(guān)聯(lián):
$factory->afterMaking(App\User::class, function ($user, $faker) { // ... }); $factory->afterCreating(App\User::class, function ($user, $faker) { $user->accounts()->save(factory(App\Account::class)->make()); });
您還可以為 工廠狀態(tài) 定義回調(diào):
$factory->afterMakingState(App\User::class, 'delinquent', function ($user, $faker) { // ... }); $factory->afterCreatingState(App\User::class, 'delinquent', function ($user, $faker) { // ... });
使用模型工廠
創(chuàng)建模型
模型工廠定義后,就可以在測(cè)試或種子文件中使用全局 factory
函數(shù)來(lái)生成模型實(shí)例。 那么,讓我們來(lái)看一些創(chuàng)建模型的例子。 首先,我們將使用 make
方法創(chuàng)建模型,但不將它們保存到數(shù)據(jù)庫(kù)中:
public function testDatabase(){ $user = factory(App\User::class)->make(); //在測(cè)試中使用模型... }
您還可以創(chuàng)建許多模型的集合或創(chuàng)建給定類型的模型:
//創(chuàng)建三個(gè) App\User 實(shí)例... $users = factory(App\User::class, 3)->make();
應(yīng)用狀態(tài)
您也可以將任何 states 應(yīng)用于模型。 如果要將多個(gè)狀態(tài)轉(zhuǎn)換應(yīng)用于模型,則應(yīng)指定要應(yīng)用的每個(gè)狀態(tài)的名稱:
$users = factory(App\User::class, 5)->states('delinquent')->make(); $users = factory(App\User::class, 5)->states('premium', 'delinquent')->make();
覆蓋屬性
如果要覆蓋模型的某些默認(rèn)值,可以將一組值傳遞給 make
方法。 只有指定的值才會(huì)被替換,而其余值仍然設(shè)置為工廠指定的默認(rèn)值:
$user = factory(App\User::class)->make([ 'name' => 'Abigail', ]);
持久化模型
create
方法不僅創(chuàng)建了模型實(shí)例,還使用 Eloquent 的 save
方法將它們保存到數(shù)據(jù)庫(kù)中:
public function testDatabase(){ // 創(chuàng)建單個(gè) App\User 實(shí)例... $user = factory(App\User::class)->create(); // 創(chuàng)建3個(gè) App\User 實(shí)例.. $users = factory(App\User::class, 3)->create(); // 在測(cè)試中使用模型... }
您可以通過(guò)將數(shù)組傳遞給 create
方法來(lái)覆蓋模型上的屬性:
$user = factory(App\User::class)->create([ 'name' => 'Abigail', ]);
關(guān)聯(lián)
在這個(gè)例子中,我們將附加一些創(chuàng)建模型的關(guān)系。 當(dāng)使用 create
方法創(chuàng)建多個(gè)模型時(shí),返回一個(gè) Eloquent 集合實(shí)例 ,這樣就可以在集合上使用 each 等便利方法:
$users = factory(App\User::class, 3) ->create() ->each(function ($user) { $user->posts()->save(factory(App\Post::class)->make()); });
關(guān)聯(lián) & 屬性閉包
您還可以使用工廠定義中的 Closure 屬性將關(guān)系附加到模型。 例如,如果您想在創(chuàng)建 Post
時(shí)創(chuàng)建一個(gè)新的 User
實(shí)例,您可以執(zhí)行以下操作:
$factory->define(App\Post::class, function ($faker) { return [ 'title' => $faker->title, 'content' => $faker->paragraph, 'user_id' => function () { return factory(App\User::class)->create()->id; } ]; });
這些閉包接收一個(gè)包含工廠屬性的數(shù)組:
$factory->define(App\Post::class, function ($faker) { return [ 'title' => $faker->title, 'content' => $faker->paragraph, 'user_id' => function () { return factory(App\User::class)->create()->id; }, 'user_type' => function (array $post) { return App\User::find($post['user_id'])->type; } ]; });
可用的斷言方法
Laravel 為 PHPUnit 測(cè)試提供了多個(gè)數(shù)據(jù)庫(kù)斷言方法:
方法 | 描述 |
---|---|
$this->assertDatabaseHas($table, array $data); | 斷言數(shù)據(jù)庫(kù)表中包含給定的數(shù)據(jù)。 |
$this->assertDatabaseMissing($table, array $data); | 斷言數(shù)據(jù)庫(kù)表中不包含給定的數(shù)據(jù)。 |
$this->assertSoftDeleted($table, array $data); | 斷言數(shù)據(jù)庫(kù)中的指定記錄已軟刪除。 |