# Fixtures
Fixtures 是測試中非常重要的一部分。他們的主要目的是建立一個固定/已知的環境狀態以確保 測試可重復并且按照預期方式運行。Yii 提供一個簡單可用的 Fixure 框架 允許你精確的定義你的 Fixtures 。
Yii 的 Fixture 框架的核心概念稱之為?*fixture object*?。一個 Fixture object 代表 一個測試環境的某個特定方面,它是 yii\test\Fixture 或者其子類的實例。 比如,你可以使用?`UserFixture`?來確保用戶DB表包含固定的數據。 你在運行一個測試之前加載一個或者多個 fixture object,并在結束后卸載他們。
一個 Fixture 可能依賴于其他的 Fixtures ,通過它的 yii\test\Fixture::depends 來指定。 當一個 Fixture 被加載前,它依賴的 Fixture 會被自動的加載;同樣,當某個 Fixture 被卸載后, 它依賴的 Fixtures 也會被自動的卸載。
## 定義一個 Fixture
為了定義一個 Fixture,你需要創建一個新的 class 繼承自 yii\test\Fixture 或者 yii\test\ActiveFixture 。前一個類對于一般用途的 Fixture 比較適合, 而后者則有一些增強功能專用于與數據庫和 ActiveRecord 一起協作。
下面的代碼定義一個關于?`User`?ActiveRecord 和相關的用戶表的 Fixture:
~~~
<?php
namespace app\tests\fixtures;
use yii\test\ActiveFixture;
class UserFixture extends ActiveFixture
{
public $modelClass = 'app\models\User';
}
~~~
> 技巧:每個?`ActiveFixture`?都會準備一個 DB 表用來測試。你可以通過設置 yii\test\ActiveFixture::tableName 或 yii\test\ActiveFixture::modelClass 屬性來指定具體的表。如果是后者, 表名會從?`modleClass`?指定的?`ActiveRecord`?中獲取。
> 注意:yii\test\ActiveFixture 僅限于 SQL 數據庫,對于 NoSQL 數據庫, Yii 提供以下?`ActiveFixture`?類:
>
> * Mongo DB: yii\mongodb\ActiveFixture
> * Elasticsearch: yii\elasticsearch\ActiveFixture (since version 2.0.2)
提供給?`ActiveFixture`?的 fixture data 通常放在一個路徑為?`FixturePath/data/TableName.php`?的文件中, 其中?`FixturePath`代表 Fixture 類所在的路徑,?`TableName`?則是和 Fixture 關聯的表。在以上的例子中, 這個文件應該是`@app/tests/fixtures/data/user.php`?。 data 文件返回一個包含要被插入用戶表中的數據文件,比如:
~~~
<?php
return [
'user1' => [
'username' => 'lmayert',
'email' => 'strosin.vernice@jerde.com',
'auth_key' => 'K3nF70it7tzNsHddEiq0BZ0i-OU8S3xV',
'password' => '$2y$13$WSyE5hHsG1rWN2jV8LRHzubilrCLI5Ev/iK0r3jRuwQEs2ldRu.a2',
],
'user2' => [
'username' => 'napoleon69',
'email' => 'aileen.barton@heaneyschumm.com',
'auth_key' => 'dZlXsVnIDgIzFgX4EduAqkEPuphhOh9q',
'password' => '$2y$13$kkgpvJ8lnjKo8RuoR30ay.RjDf15bMcHIF7Vz1zz/6viYG5xJExU6',
],
];
~~~
你可以給某行指定一個 alias 別名,這樣在你以后的測試中,你可以通過別名來確定某行。 在上面的例子中,這兩行指定別名為`user1`?和?`user2`。
同樣,你不需要特別的為自動增長(auto-incremental)的列指定數據, Yii 將會在 Fixture 被加載時自動的填充正確的列值到這些行中。
> 技巧:你可以通過設置 yii\test\ActiveFixture::dataFile 屬性來自定義 data 文件的位置。 同樣,你可以重寫 yii\test\ActiveFixture::getData() 來提供數據。
如之前所述,一個 Fixture 可以依賴于其他的 Fixture 。比如一個?`UserProfileFixture`?可能需要依賴于?`UserFixture`, 因為 user profile 表包括一個指向 user 表的外鍵。那么, 這個依賴關系可以通過 yii\test\Fixture::depends 屬性來指定,比如如下:
~~~
namespace app\tests\fixtures;
use yii\test\ActiveFixture;
class UserProfileFixture extends ActiveFixture
{
public $modelClass = 'app\models\UserProfile';
public $depends = ['app\tests\fixtures\UserFixture'];
}
~~~
依賴關系確保所有的 Fixtures 能夠以正常的順序被加載和卸載。在以上的例子中, 為確保外鍵存在,?`UserFixture`?會在`UserProfileFixture`?之前加載, 同樣,也會在其卸載后同步卸載。
在上面,我們展示了如何定義一個 DB 表的 Fixture 。為了定義一個與 DB 無關的 Fixture (比如一個fixture關于文件和路徑的),你可以從一個更通用的基類 yii\test\Fixture 繼承, 并重寫 yii\test\Fixture::load() 和 yii\test\Fixture::unload() 方法。
## 使用 Fixtures
如果你使用?[CodeCeption](http://codeception.com/)?作為你的 Yii 代碼測試框架, 你需要考慮使用?`yii2-codeception`?擴展,這個擴展包含內置的機制來支持加載和訪問 Fixtures。 如果你使用其他的測試框架,為了達到加載和訪問 Fixture 的目的, 你需要在你的測試用例中使用 yii\test\FixtureTrait。
在以下示例中,我們會展示如何通過?`yii2-codeception`?寫一個?`UserProfile`?單元來測試某個 class。
在一個繼承自 yii\codeception\DbTestCase 或者 yii\codeception\TestCase 的單元測試類中, 你可以在 yii\test\FixtureTrait::fixtures() 方法中聲明你希望使用哪個 Fixture。比如:
~~~
namespace app\tests\unit\models;
use yii\codeception\DbTestCase;
use app\tests\fixtures\UserProfileFixture;
class UserProfileTest extends DbTestCase
{
public function fixtures()
{
return [
'profiles' => UserProfileFixture::className(),
];
}
// ...test methods...
}
~~~
在測試用例的每個測試方法運行前?`fixtures()`?方法列表返回的 Fixture 會被自動的加載, 并在結束后自動的卸載。同樣,如前面所述,當一個 Fixture 被加載之前, 所有它依賴的 Fixture 也會被自動的加載。在上面的例子中,因為?`UserProfileFixture`?依賴于`UserFixtrue`,當運行測試類中的任意測試方法時, 兩個 Fixture,`UserFixture`?和?`UserProfileFixture`?會被依序加載。
當我們通過?`fixtures()`?方法指定需要加載的 Fixture 時,我們既可以使用一個類名, 也可以使用一個配置數組。配置數組可以讓你自定義加載的 fixture 的屬性名。
你同樣可以給一個 Fixture 指定一個別名(alias),在上面的例子中,`UserProfileFixture`?的別名為?`profiles`?。 在測試方法中,你可以通過別名來訪問一個 Fixture 對象。比如,?`$this->profiles`?將會返回?`UserProfileFixture`?對象。
因為?`UserProfileFixture`?從?`ActiveFixture`?處繼承, 在后面,你可以通過如下的語法形式來訪問 Fixture 提供的數據:
~~~
// returns the data row aliased as 'user1'
$row = $this->profiles['user1'];
// returns the UserProfile model corresponding to the data row aliased as 'user1'
$profile = $this->profiles('user1');
// traverse every data row in the fixture
foreach ($this->profiles as $row) ...
~~~
> 注:?`$this->profiles`?的類型仍為?`UserProfileFixture`, 以上的例子是通過 PHP 魔術方法來實現的。
## 定義和使用全局 Fixtures
以上示例的 Fixture 主要用于單個的測試用例, 在許多情況下,你需要使用一些全局的 Fixture 來讓所有或者大量的測試用例使用。 yii\test\InitDbFixture 就是這樣的一個例子,它主要做兩個事情:
* 它通過執行在?`@app/tests/fixtures/initdb.php`?里的腳本來做一些通用的初始化任務;
* 在加載其他的 DB Fixture 之前禁用數據庫完整性校驗,同時在其他的 DB Fixture 卸載后啟用數據庫完整性校驗。
全局的 Fixture 和非全局的用法類似,唯一的區別是你在 yii\codeception\TestCase::globalFixtures() 中聲明它, 而非?`fixtures()`?方法。當一個測試用例加載 Fixture 時, 它首先加載全局的 Fixture,然后才是非全局的。
yii\codeception\DbTestCase 默認已經在其?`globalFixtures()`?方法中聲明了?`InitDbFixture`, 這意味著如果你想要在每個測試之前執行一些初始化工作,你只需要調整?`@app/tests/fixtures/initdb.php`?中的代碼即可。 你只需要簡單的集中精力中開發單個的測試用例和相關的 Fixture。
## 組織 Fixture 類和相關的數據文件
默認情況下,Fixture 類會在其所在的目錄下面的?`data`?子目錄尋找相關的數據文件。 在一些簡單的項目中,你可以遵循此范例。對于一些大項目, 您可能經常為同一個?Fixture?類的不同測試而切換不同的數據文件。 在這種情況下,我們推薦你按照一種類似于命名空間 的方式有層次地組織你的數據文件,比如:
~~~
# under folder tests\unit\fixtures
data\
components\
fixture_data_file1.php
fixture_data_file2.php
...
fixture_data_fileN.php
models\
fixture_data_file1.php
fixture_data_file2.php
...
fixture_data_fileN.php
# and so on
~~~
這樣,你就可以避免在測試用例之間產生沖突,并根據你的需要使用它們。
> 注意:在以上的例子中,Fixture 文件只用于示例目的。在真實的環境下,你需要根據你的 Fixture 類繼承的基類來決定它們的命名。 比如,如果你從 yii\test\ActiveFixture 繼承了一個 DB Fixture, 你需要用數據庫表名字作為 Fixture 的數據文件名;如果你從 yii\mongodb\ActiveFixture 繼承了一個 MongoDB Fixture, 你需要使用 collection 名作為文件名。
組織 Fixuture 類名的方式同樣可以使用前述的層次組織法,但是,為了避免跟數據文件產生沖突, 你需要用?`fixtures`?作為根目錄而非?`data`。
## 總結
> 注意:本節內容正在開發中。
在上面,我們描述了如何定義和使用 Fixture,在下面,我們將總結出一個 標準地運行與 DB 有關的單元測試的規范工作流程:
1. 使用?`yii migrate`?工具來讓你的測試數據庫更新到最新的版本;
2. 運行一個測試:
* 加載 Fixture:清空所有的相關表結構,并用 Fixture 數據填充
* 執行真實的測試用例
* 卸載 Fixture
3. 重復步驟2直到所有的測試結束。
**以下部分即將被清除**
# 管理 Fixture
>注: 本章節正在開發中 > > todo: 以下部分將會與 test-fixtures.md 合并。
Fixture 是測試中很很重要的一個部分,它們的主要目的是為你提供不同的測試所需要的數據。 因為這些數據,你的測試將會更高效和有用。
Yii 通過?`yii fixture`?命令行工具來支持 Fixture,這個工具支持:
* 把 Fixture 加載到不同的存儲工具中,比如:RDBMS,NoSQL 等;
* 以不同的方式卸載 Fixture(通常是清空存儲);
* 自動的生成 Fixutre 并用隨機數據填充。
## Fixtures 格式
Fixtures 是不同方法和配置的對象, 官方引用[documentation](https://github.com/yiisoft/yii2/blob/master/docs/guide/test-fixture.md)?。 讓我們假設有 Fixtures 數據加載:
~~~
#Fixtures 數據目錄的 users.php 文件,默認 @tests\unit\fixtures\data
return [
[
'name' => 'Chase',
'login' => 'lmayert',
'email' => 'strosin.vernice@jerde.com',
'auth_key' => 'K3nF70it7tzNsHddEiq0BZ0i-OU8S3xV',
'password' => '$2y$13$WSyE5hHsG1rWN2jV8LRHzubilrCLI5Ev/iK0r3jRuwQEs2ldRu.a2',
],
[
'name' => 'Celestine',
'login' => 'napoleon69',
'email' => 'aileen.barton@heaneyschumm.com',
'auth_key' => 'dZlXsVnIDgIzFgX4EduAqkEPuphhOh9q',
'password' => '$2y$13$kkgpvJ8lnjKo8RuoR30ay.RjDf15bMcHIF7Vz1zz/6viYG5xJExU6',
],
];
~~~
如果我們使用 Fixture 將數據加載到數據庫,那么這些行將被應用于?`users`?表。 如果我們使用 nosql Fixtures ,例如?`mongodb`fixture,那么這些數據將應用于?`users`?mongodb 集合。 為了了解更多實現各種加載策略,訪問官網?[documentation](https://github.com/yiisoft/yii2/blob/master/docs/guide/test-fixture.md)。 上面的 Fixture 案例是由?`yii2-faker`?擴展自動生成的, 在這里了解更多?[section](http://www.yiichina.com/doc/guide/2.0/test-fixtures#auto-generating-fixtures)。 Fixture 類名不應該是復數形式。
## 加載 Fixtures
Fixture 類應該以?`Fixture`?類作為后綴。默認的 Fixtures 能在?`tests\unit\fixtures`?命名空間下被搜索到, 你可以通過配置和命名行選項來更改這個行為,你可以通過加載或者卸載指定它名字前面的`-`來排除一些 Fixtures,像?`-User`。
運行如下命令去加載 Fixture:
~~~
yii fixture/load <fixture_name>
~~~
必需參數?`fixture_name`?指定一個將被加載數據的 Fixture 名字。 你可以同時加載多個 Fixtures 。 以下是這個命令的正確格式:
~~~
// load `User` fixture
yii fixture/load User
// same as above, because default action of "fixture" command is "load"
yii fixture User
// load several fixtures
yii fixture User UserProfile
// load all fixtures
yii fixture/load "*"
// same as above
yii fixture "*"
// load all fixtures except ones
yii fixture "*" -DoNotLoadThisOne
// load fixtures, but search them in different namespace. By default namespace is: tests\unit\fixtures.
yii fixture User --namespace='alias\my\custom\namespace'
// load global fixture `some\name\space\CustomFixture` before other fixtures will be loaded.
// By default this option is set to `InitDbFixture` to disable/enable integrity checks. You can specify several
// global fixtures separated by comma.
yii fixture User --globalFixtures='some\name\space\Custom'
~~~
## 卸載 Fixtures
運行如下命名去卸載 Fixtures:
~~~
// unload Users fixture, by default it will clear fixture storage (for example "users" table, or "users" collection if this is mongodb fixture).
yii fixture/unload User
// Unload several fixtures
yii fixture/unload User,UserProfile
// unload all fixtures
yii fixture/unload "*"
// unload all fixtures except ones
yii fixture/unload "*" -DoNotUnloadThisOne
~~~
同樣的命名選項:?`namespace`,?`globalFixtures`?也可以應用于該命令。
## 全局命令配置
當命令行選項允許我們配置遷移命令, 有時我們只想配置一次。例如, 你可以按照如下配置遷移目錄:
~~~
'controllerMap' => [
'fixture' => [
'class' => 'yii\console\controllers\FixtureController',
'namespace' => 'myalias\some\custom\namespace',
'globalFixtures' => [
'some\name\space\Foo',
'other\name\space\Bar'
],
],
]
~~~
## 自動生成 fixtures
Yii 還可以為你自動生成一些基于一些模板的 Fixtures。 你能夠以不同語言格式用不同的數據生成你的 Fixtures。 這些特征由?[Faker](https://github.com/fzaninotto/Faker)?庫和?`yii2-faker`?擴展完成。 關注?[guide](https://github.com/yiisoft/yii2-faker)?擴展獲取更多的文檔。
- 介紹(Introduction)
- 關于 Yii(About Yii)
- 從 Yii 1.1 升級(Upgrading from Version 1.1)
- 入門(Getting Started)
- 安裝 Yii(Installing Yii)
- 運行應用(Running Applications)
- 第一次問候(Saying Hello)
- 使用 Forms(Working with Forms)
- 玩轉 Databases(Working with Databases)
- 用 Gii 生成代碼(Generating Code with Gii)
- 更上一層樓(Looking Ahead)
- 應用結構(Application Structure)
- 結構概述(Overview)
- 入口腳本(Entry Scripts)
- 應用(Applications)
- 應用組件(Application Components)
- 控制器(Controllers)
- 模型(Models)
- 視圖(Views)
- 模塊(Modules)
- 過濾器(Filters)
- 小部件(Widgets)
- 前端資源(Assets)
- 擴展(Extensions)
- 請求處理(Handling Requests)
- 運行概述(Overview)
- 引導(Bootstrapping)
- 路由引導與創建 URL(Routing and URL Creation)
- 請求(Requests)
- 響應(Responses)
- Sessions and Cookies
- 錯誤處理(Handling Errors)
- 日志(Logging)
- 關鍵概念(Key Concepts)
- 組件(Components)
- 屬性(Properties)
- 事件(Events)
- 行為(Behaviors)
- 配置(Configurations)
- 別名(Aliases)
- 類自動加載(Class Autoloading)
- 服務定位器(Service Locator)
- 依賴注入容器(Dependency Injection Container)
- 配合數據庫工作(Working with Databases)
- 數據庫訪問(Data Access Objects): 數據庫連接、基本查詢、事務和模式操作
- 查詢生成器(Query Builder): 使用簡單抽象層查詢數據庫
- 活動記錄(Active Record): 活動記錄對象關系映射(ORM),檢索和操作記錄、定義關聯關系
- 數據庫遷移(Migrations): 在團體開發中對你的數據庫使用版本控制
- Sphinx
- Redis
- MongoDB
- ElasticSearch
- 接收用戶數據(Getting Data from Users)
- 創建表單(Creating Forms)
- 輸入驗證(Validating Input)
- 文件上傳(Uploading Files)
- 收集列表輸入(Collecting Tabular Input)
- 多模型同時輸入(Getting Data for Multiple Models)
- 顯示數據(Displaying Data)
- 格式化輸出數據(Data Formatting)
- 分頁(Pagination)
- 排序(Sorting)
- 數據提供器(Data Providers)
- 數據小部件(Data Widgets)
- 操作客戶端腳本(Working with Client Scripts)
- 主題(Theming)
- 安全(Security)
- 認證(Authentication)
- 授權(Authorization)
- 處理密碼(Working with Passwords)
- 客戶端認證(Auth Clients)
- 安全領域的最佳實踐(Best Practices)
- 緩存(Caching)
- 概述(Overview)
- 數據緩存(Data Caching)
- 片段緩存(Fragment Caching)
- 分頁緩存(Page Caching)
- HTTP 緩存(HTTP Caching)
- RESTful Web 服務
- 快速入門(Quick Start)
- 資源(Resources)
- 控制器(Controllers)
- 路由(Routing)
- 格式化響應(Response Formatting)
- 授權驗證(Authentication)
- 速率限制(Rate Limiting)
- 版本化(Versioning)
- 錯誤處理(Error Handling)
- 開發工具(Development Tools)
- 調試工具欄和調試器(Debug Toolbar and Debugger)
- 使用 Gii 生成代碼(Generating Code using Gii)
- TBD 生成 API 文檔(Generating API Documentation)
- 測試(Testing)
- 概述(Overview)
- 搭建測試環境(Testing environment setup)
- 單元測試(Unit Tests)
- 功能測試(Functional Tests)
- 驗收測試(Acceptance Tests)
- 測試夾具(Fixtures)
- 高級專題(Special Topics)
- 高級應用模版(Advanced Project Template)
- 從頭構建自定義模版(Building Application from Scratch)
- 控制臺命令(Console Commands)
- 核心驗證器(Core Validators)
- 國際化(Internationalization)
- 收發郵件(Mailing)
- 性能優化(Performance Tuning)
- 共享主機環境(Shared Hosting Environment)
- 模板引擎(Template Engines)
- 集成第三方代碼(Working with Third-Party Code)
- 小部件(Widgets)
- Bootstrap 小部件(Bootstrap Widgets)
- jQuery UI 小部件(jQuery UI Widgets)
- 助手類(Helpers)
- 助手一覽(Overview)
- Array 助手(ArrayHelper)
- Html 助手(Html)
- Url 助手(Url)