# Laravel 測試之 - 數據庫測試
- [簡介](#introduction)
- [每次測試后重置數據庫](#resetting-the-database-after-each-test)
- [使用遷移](#using-migrations)
- [使用事務](#using-transactions)
- [創建模型工廠](#writing-factories)
- [工廠狀態](#factory-states)
- [在測試中使用模型工廠](#using-factories)
- [創建模型](#creating-models)
- [持久化模型](#persisting-models)
- [模型關聯](#relationships)
- [可用的斷言方法](#available-assertions)
<a name="introduction"></a>
## 簡介
Laravel 提供了多種有用的工具來讓你更容易的測試使用數據庫的應用程序。首先,你可以使用 `assertDatabaseHas` 輔助函數,來斷言數據庫中是否存在與指定條件互相匹配的數據。舉例來說,如果我們想驗證 `users` 數據表中是否存在 `email` 值為 `sally@example.com` 的數據,我們可以按照以下的方式來做測試:
public function testDatabase()
{
// 創建調用至應用程序...
$this->assertDatabaseHas('users', [
'email' => 'sally@example.com'
]);
}
你也可以使用 `assertDatabaseMissing` 輔助函數來斷言數據不在數據庫中。
當然,使用 `assertDatabaseHas` 方法及其它的輔助函數只是為了方便。你也可以隨意使用 PHPUnit 內置的所有斷言方法來擴充測試。
<a name="resetting-the-database-after-each-test"></a>
## 每次測試后重置數據庫
在每次測試結束后都需要對數據進行重置,這樣前面的測試數據就不會干擾到后面的測試。
<a name="using-migrations"></a>
### 使用遷移
其中有一種方式就是在每次測試后都還原數據庫,并在下次測試前運行遷移。Laravel 提供了簡潔的 `DatabaseMigrations` trait,它會自動幫你處理好這些操作。你只需在測試類中使用此 trait 即可:
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class ExampleTest extends TestCase
{
use DatabaseMigrations;
/**
* 基本的功能測試示例。
*
* @return void
*/
public function testBasicExample()
{
$response = $this->get('/');
// ...
}
}
<a name="using-transactions"></a>
### 使用事務
另一個方式,就是將每個測試案例都包含在數據庫事務中。Laravel 提供了一個簡潔的 `DatabaseTransactions` trait 來自動幫你處理好這些操作。
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class ExampleTest extends TestCase
{
use DatabaseTransactions;
/**
* 基本的功能測試示例。
*
* @return void
*/
public function testBasicExample()
{
$response = $this->get('/');
// ...
}
}
> {note} 此 trait 的事務只包含默認的數據庫連接。 如果你的應用程序使用多個數據庫連接,你需要在你的測試類中定義一個 `$connectionsToTransact` 屬性,然后你就可以把你測試中需要用到的數據庫連接名稱以數組的形式放到這個屬性中。
<a name="writing-factories"></a>
## 創建模型工廠
測試時,常常需要在運行測試之前寫入一些數據到數據庫中。創建測試數據時,除了手動的來設置每個字段的值,還可以使用 [Eloquent 模型](/docs/{{version}}/eloquent) 的「工廠」來設置每個屬性的默認值。在開始之前,你可以先查看下應用程序的 `database/factories/UserFactory.php` 文件。此文件包含一個現成的模型工廠定義:
$factory->define(App\User::class, function (Faker\Generator $faker) {
static $password;
return [
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
'password' => $password ?: $password = bcrypt('secret'),
'remember_token' => str_random(10),
];
});
閉包內為模型工廠的定義,你可以返回模型中所有屬性的默認測試值。在該閉包內會接收到 [Faker](https://github.com/fzaninotto/Faker) PHP 函數庫的實例,它可以讓你很方便的生成各種隨機數據以進行測試。
為了更好的組織代碼,你也可以自己為每個數據模型創建對應的模型工廠類。比如說,你可以在 `database/factories` 文件夾下創建 `UserFactory.php` 和 `CommentFactory.php` 文件。在 `factories` 目錄中的文件都會被 Laravel 自動加載。
<a name="factory-states"></a>
### 工廠狀態
工廠狀態可以讓你任意組合你的模型工廠,僅需要做出適當差異化的修改,就可以達到讓模型擁有多種不同的狀態。例如,你的 `用戶` 模型中可以修改某個默認屬性值來達到標識一種 `欠款` 的狀態。你可以使用 `state` 方法來進行這種狀態轉換。對于簡單的工廠狀態,你可以直接傳入要修改的屬性數組。
$factory->state(App\User::class, 'delinquent', [
'account_status' => 'delinquent',
]);
如果你的工廠狀態需要計算或者需要使用 `$faker` 實例,你可以使用閉包方法來實現狀態屬性的修改:
$factory->state(App\User::class, 'address', function ($faker) {
return [
'address' => $faker->address,
];
});
<a name="using-factories"></a>
## 在測試中使用模型工廠
<a name="creating-models"></a>
### 創建模型
在模型工廠定義后,就可以在測試或是數據庫的填充文件中,通過全局的 `factory` 函數來生成模型實例。接著讓我們先來看看幾個創建模型的例子。首先我們會使用 `make` 方法創建模型,但不將它們保存至數據庫:
public function testDatabase()
{
$user = factory(App\User::class)->make();
// 在測試中使用模型...
}
你也可以創建一個含有多個模型的集合,或創建一個指定類型的模型:
// 創建一個 App\User 實例
$users = factory(App\User::class, 3)->make();
#### 應用模型工廠狀態
你可能需要在你的模型中應用不同的 [模型工廠狀態](#factory-states)。如果你想模型加上多種不同的狀態,你只須指定每個你想添加的狀態名稱即可:
$users = factory(App\User::class, 5)->states('delinquent')->make();
$users = factory(App\User::class, 5)->states('premium', 'delinquent')->make();
#### 重寫模型屬性
如果你想重寫模型中的某些默認值,則可以傳遞一個包含數值的數組至 `make` 方法。只有指定的數值會被替換,其它剩余的數值則會按照模型工廠指定的默認值來設置:
$user = factory(App\User::class)->make([
'name' => 'Abigail',
]);
<a name="persisting-models"></a>
### 持久化模型
`create` 方法不僅會創建模型實例,同時會使用 Eloquent 的 `save` 方法來將它們保存至數據庫:
public function testDatabase()
{
// 創建一個 App\User 實例
$user = factory(App\User::class)->create();
// 創建 3 個 App\User 實例
$users = factory(App\User::class, 3)->create();
// 在測試中使用模型...
}
同樣的,你可以在數組傳遞至 `create` 方法時重寫模型的屬性
$user = factory(App\User::class)->create([
'name' => 'Abigail',
]);
<a name="relationships"></a>
### 模型關聯
在本例中,我們還會增加關聯至我們所創建的模型。當使用 `create` 方法創建多個模型時,它會返回一個 Eloquent [集合實例](/docs/{{version}}/eloquent-collections),讓你能夠使用集合所提供的便利函數,像是 `each`:
$users = factory(App\User::class, 3)
->create()
->each(function ($u) {
$u->posts()->save(factory(App\Post::class)->make());
});
#### 關聯和屬性閉包
你可以使用閉包參數來創建模型關聯。例如如果你想在創建一個 `Post` 的順便創建一個 `User` 實例:
$factory->define(App\Post::class, function ($faker) {
return [
'title' => $faker->title,
'content' => $faker->paragraph,
'user_id' => function () {
return factory(App\User::class)->create()->id;
}
];
});
這些閉包也可以獲取到生成的模型工廠包含的屬性數組:
$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;
}
];
});
<a name="available-assertions"></a>
## 可用的斷言方法
Laravel 為你的 [PHPUnit](https://phpunit.de/) 測試提供了一些數據庫斷言方法:
方法名 | 描述
------------- | -------------
`$this->assertDatabaseHas($table, array $data);` | 斷言數據庫里含有指定表。
`$this->assertDatabaseMissing($table, array $data);` | 斷言表里沒有指定數據。
`$this->assertSoftDeleted($table, array $data);` | 斷言指定記錄已經被軟刪除。
---
> {note} 歡迎任何形式的轉載,但請務必注明出處,尊重他人勞動共創開源社區。
>
> 轉載請注明:本文檔由 Laravel China 社區 [laravel-china.org](https://laravel-china.org) 組織翻譯,詳見 [翻譯召集帖](https://laravel-china.org/topics/5756/laravel-55-document-translation-call-come-and-join-the-translation)。
>
> 文檔永久地址: https://d.laravel-china.org
- 說明
- 翻譯說明
- 發行說明
- 升級說明
- 貢獻導引
- 入門指南
- 安裝
- 配置信息
- 文件夾結構
- HomeStead
- Valet
- 核心架構
- 請求周期
- 服務容器
- 服務提供者
- 門面(Facades)
- Contracts
- 基礎功能
- 路由
- 中間件
- CSRF 保護
- 控制器
- 請求
- 響應
- 視圖
- 重定向
- Session
- 表單驗證
- 錯誤與日志
- 前端開發
- Blade 模板
- 本地化
- 前端指南
- 編輯資源 Mix
- 安全
- 用戶認證
- API認證
- 用戶授權
- 加密解密
- 哈希
- 重置密碼
- 綜合話題
- Artisan 命令行
- 廣播系統
- 緩存系統
- 集合
- 事件系統
- 文件存儲
- 輔助函數
- 郵件發送
- 消息通知
- 擴展包開發
- 隊列
- 任務調度
- 數據庫
- 快速入門
- 查詢構造器
- 分頁
- 數據庫遷移
- 數據填充
- Redis
- Eloquent ORM
- 快速入門
- 模型關聯
- Eloquent 集合
- 修改器
- API 資源
- 序列化
- 測試
- 快速入門
- HTTP 測試
- 瀏覽器測試 Dusk
- 數據庫測試
- 測試模擬器
- 官方擴展包
- Cashier 交易工具包
- Envoy 部署工具
- Horizon
- Passport OAuth 認證
- Scout 全文搜索
- Socialite 社交化登錄
- 交流說明