diff --git a/docs/guide/console-fixture.md b/docs/guide/console-fixture.md
index 393a693..95b8d2c 100644
--- a/docs/guide/console-fixture.md
+++ b/docs/guide/console-fixture.md
@@ -37,7 +37,7 @@ return [
 ];
 ```
 
-This data will be loaded to the `users`, but before it will be loaded table `users` will be cleared: all data deleted, sequence reseted.
+This data will be loaded to the `users`, but before it will be loaded table `users` will be cleared: all data deleted, sequence reset.
 Above fixture example was auto-generated by `yii2-faker` extension, read more about it in these [section](#auto-generating-fixtures).
 
 Applying fixtures
diff --git a/docs/guide/test-fixture.md b/docs/guide/test-fixture.md
new file mode 100644
index 0000000..b1941e8
--- /dev/null
+++ b/docs/guide/test-fixture.md
@@ -0,0 +1,196 @@
+Fixtures
+========
+
+Fixtures are important part of testing. Their main purpose is to set up the environment in a fixed/known state
+so that your tests are repeatable and run in an expected way. Yii provides a fixture framework that allows
+you to define your fixtures precisely and use them easily.
+
+A key concept in the Yii fixture framework is the so-called *fixture objects*. A fixture object is an instance
+of [[yii\test\Fixture]] or its child class. It represents a particular aspect of a test environment. For example,
+you may define `UserFixture` to create the user table and populate it with some known data. You load one or multiple
+fixture objects before running a test and unload them when finishing.
+
+A fixture may depend on other fixtures, specified via its [[yii\test\Fixture::depends]] property.
+When a fixture is being loaded, the fixtures it depends on will be automatically loaded BEFORE the fixture;
+and when the fixture is being unloaded, the dependent fixtures will be unloaded AFTER the fixture.
+
+
+Defining a Fixture
+------------------
+
+To define a fixture, create a new class by extending [[yii\test\Fixture]] or [[yii\test\ActiveFixture]].
+The former is best suited for general purpose fixtures, while the latter has enhanced features specifically
+designed to work with database and ActiveRecord.
+
+If you extend from [[yii\test\Fixture]], you should normally override the [[yii\test\Fixture::load()]] method
+with your custom code of setting up the test environment (e.g. creating specific directories or files).
+In the following, we will mainly describe how to define a database fixture by extending [[yii\test\ActiveFixture]].
+
+Each `ActiveFixture` is about preparing a DB table for testing purpose. You may specify the table
+by setting either the [[yii\test\ActiveFixture::tableName]] property or the [[yii\test\ActiveFixture::modelClass]]
+property. If the latter, the table name will be taken from the `ActiveRecord` class specified by `modelClass`.
+
+```php
+<?php
+namespace app\tests\fixtures;
+
+use yii\test\ActiveFixture;
+
+class UserFixture extends ActiveFixture
+{
+	public $modelClass = 'app\models\User';
+}
+```
+
+Next, you should override [[yii\test\ActiveFixture::loadSchema()]] to create the table. You may wonder why we need
+to create the table when loading a fixture and why we do not work with a database which already has the table. This
+is because preparing a complete test database is often very time consuming and in most test cases, only a very tiny part
+of the database is touched. So the idea here is to create the table only when it is needed by the test.
+
+```php
+<?php
+namespace app\tests\fixtures;
+
+use yii\test\ActiveFixture;
+
+class UserFixture extends ActiveFixture
+{
+	public $modelClass = 'app\models\User';
+
+	protected function loadSchema()
+	{
+		$this->createTable('tbl_user', [
+			'username' => 'string not null',
+			'email' => 'string not null',
+			...
+		]);
+	}
+}
+```
+
+Lastly, you should provide the fixture data in a file located at `FixturePath/data/TableName.php`,
+where `FixturePath` stands for the directory containing the fixture class file, and `TableName`
+is the name of the table associated with the fixture. In the example above, the file should be
+`@app/tests/fixtures/data/tbl_user.php`. The data file should return an array of data rows
+to be inserted into the user table. For example,
+
+```php
+<?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',
+	],
+];
+```
+
+You may give an alias to a row so that later in your test, you may refer to the row via the alias. In the above example,
+the two rows are aliased as `user1` and `user2`, respectively.
+
+Also, you do not need to specify the data for auto-incremental columns. Yii will automatically fill the actual
+values into the rows when the fixture is being loaded.
+
+> Tip: You may customize the location of the data file by setting the [[yii\test\ActiveFixture::dataFile]] property.
+> You may also override [[yii\test\ActiveFixture::getData()]] to provide the data.
+
+As we described earlier, a fixture may depend on other fixtures. For example, `UserProfileFixture` depends on `UserFixture`
+because the user profile table contains a foreign key pointing to the user table.
+The dependency is specified via the [[yii\test\Fixture::depends]] property, like the following,
+
+```php
+namespace app\tests\fixtures;
+
+use yii\test\ActiveFixture;
+
+class UserProfileFixture extends ActiveFixture
+{
+	public $modelClass = 'app\models\UserProfile';
+	public $depends = ['app\tests\fixtures\UserFixture'];
+}
+```
+
+
+Using Fixtures
+--------------
+
+Yii provides [[yii\test\FixtureTrait]] which can be plugged into your test classes to let you easily load and access
+fixtures. More often you would develop your test cases by using the `yii2-codeception` extension
+which uses [[yii\test\FixtureTrait]] and has the built-in support for the loading and accessing fixtures.
+In the following we will describe how to write a `UserProfile` unit test class using `yii2-codeception`.
+
+In your unit test class extending [[yii\codeception\DbTestCase]] (or [[yii\codeception\TestCase]] if you are NOT
+testing DB-related features), declare which fixtures you want to use in the [[yii\testFixtureTrait::fixtures()|fixtures()]] method.
+For example,
+
+```php
+namespace app\tests\unit\models;
+
+use yii\codeception\DbTestCase;
+use app\tests\fixtures\UserProfileFixture;
+
+class UserProfileTest extends DbTestCase
+{
+	protected function fixtures()
+	{
+		return [
+			'profiles' => UserProfileFixture::className(),
+		];
+	}
+
+	// ...test methods...
+}
+```
+
+The fixtures listed in the `fixtures()` method will be automatically loaded before running every test method
+in the test case and unloaded after finishing every test method. And as we described before, when a fixture is
+being loaded, all its dependent fixtures will be automatically loaded first. In the above example, because
+`UserProfileFixture` depends on `UserFixture`, when running any test method in the test class,
+two fixtures will be loaded sequentially: `UserFixture` and `UserProfileFixture`.
+
+When specifying fixtures in `fixtures()`, you may use either a class name or a configuration array to refer to
+a fixture. The configuration array will let you customize the fixture properties when the fixture is loaded.
+
+You may also assign an alias to a fixture. In the above example, the `UserProfileFixture` is aliased as `profiles`.
+In the test methods, you may then access a fixture object using its alias. For example, `$this->profiles` will
+return the `UserProfileFixture` object.
+
+Because `UserProfileFixture` extends from `ActiveFixture`, you may further use the following syntax to access
+the data provided by the fixture:
+
+```php
+// 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) ...
+```
+
+> Info: `$this->profiles` is still of `UserProfileFixture` type. The above access features are implemented
+> through PHP magic methods.
+
+
+Defining and Using Global Fixtures
+----------------------------------
+
+The fixtures described above are mainly used by individual test cases. In most cases, you also need some global
+fixtures that are applied to ALL or many test cases. An example is [[yii\test\InitDbFixture]] which is used to
+set up a skeleton test database and toggle database integrity checks when applying other DB fixtures.
+This fixture will try to execute a script located at `@app/tests/fixtures/initdb.php`. In this script, you may,
+for example, load a basic DB dump containing the minimal set of tables, etc.
+
+Using global fixtures is similar to using non-global ones. The only difference is that you declare these fixtures
+in [[yii\codeception\TestCase::globalFixtures()]] instead of `fixtures()`. When a test case loads fixtures, it will
+first load global fixtures and then non-global ones.
+
+By default, [[yii\codeception\DbTestCase]] already declares `InitDbFixture` in its `globalFixtures()` method.
+This means you only need to work with `@app/tests/fixtures/initdb.php` to set up your skeleton test database,
+and you can then focus on developing each individual test case and the corresponding fixtures.
diff --git a/extensions/codeception/DbTestCase.php b/extensions/codeception/DbTestCase.php
new file mode 100644
index 0000000..6730c71
--- /dev/null
+++ b/extensions/codeception/DbTestCase.php
@@ -0,0 +1,27 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\codeception;
+
+use yii\test\InitDbFixture;
+
+/**
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class DbTestCase extends TestCase
+{
+	/**
+	 * @inheritdoc
+	 */
+	protected function globalFixtures()
+	{
+		return [
+			InitDbFixture::className(),
+		];
+	}
+}
diff --git a/extensions/codeception/TestCase.php b/extensions/codeception/TestCase.php
index 212f3b7..b76c176 100644
--- a/extensions/codeception/TestCase.php
+++ b/extensions/codeception/TestCase.php
@@ -5,6 +5,7 @@ namespace yii\codeception;
 use Yii;
 use yii\base\InvalidConfigException;
 use Codeception\TestCase\Test;
+use yii\test\FixtureTrait;
 
 /**
  * TestCase is the base class for all codeception unit tests
@@ -14,6 +15,8 @@ use Codeception\TestCase\Test;
  */
 class TestCase extends Test
 {
+	use FixtureTrait;
+
 	/**
 	 * @var array|string the application configuration that will be used for creating an application instance for each test.
 	 * You can use a string to represent the file path or path alias of a configuration file.
@@ -29,6 +32,7 @@ class TestCase extends Test
 	{
 		parent::setUp();
 		$this->mockApplication();
+		$this->loadFixtures();
 	}
 
 	/**
@@ -36,6 +40,7 @@ class TestCase extends Test
 	 */
 	protected function tearDown()
 	{
+		$this->unloadFixtures();
 		$this->destroyApplication();
 		parent::tearDown();
 	}
diff --git a/framework/base/ArrayAccessTrait.php b/framework/base/ArrayAccessTrait.php
new file mode 100644
index 0000000..e4b5856
--- /dev/null
+++ b/framework/base/ArrayAccessTrait.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+/**
+ * ArrayAccessTrait provides the implementation for `IteratorAggregate`, `ArrayAccess` and `Countable`.
+ *
+ * Note that ArrayAccessTrait requires the class using it contain a property named `data` which should be an array.
+ * The data will be exposed by ArrayAccessTrait to support accessing the class object like an array.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+trait ArrayAccessTrait
+{
+	/**
+	 * Returns an iterator for traversing the data.
+	 * This method is required by the SPL interface `IteratorAggregate`.
+	 * It will be implicitly called when you use `foreach` to traverse the collection.
+	 * @return \ArrayIterator an iterator for traversing the cookies in the collection.
+	 */
+	public function getIterator()
+	{
+		return new \ArrayIterator($this->data);
+	}
+
+	/**
+	 * Returns the number of data items.
+	 * This method is required by Countable interface.
+	 * @return integer number of data elements.
+	 */
+	public function count()
+	{
+		return count($this->data);
+	}
+
+	/**
+	 * This method is required by the interface ArrayAccess.
+	 * @param mixed $offset the offset to check on
+	 * @return boolean
+	 */
+	public function offsetExists($offset)
+	{
+		return isset($this->data[$offset]);
+	}
+
+	/**
+	 * This method is required by the interface ArrayAccess.
+	 * @param integer $offset the offset to retrieve element.
+	 * @return mixed the element at the offset, null if no element is found at the offset
+	 */
+	public function offsetGet($offset)
+	{
+		return isset($this->data[$offset]) ? $this->data[$offset] : null;
+	}
+
+	/**
+	 * This method is required by the interface ArrayAccess.
+	 * @param integer $offset the offset to set element
+	 * @param mixed $item the element value
+	 */
+	public function offsetSet($offset, $item)
+	{
+		$this->data[$offset] = $item;
+	}
+
+	/**
+	 * This method is required by the interface ArrayAccess.
+	 * @param mixed $offset the offset to unset element
+	 */
+	public function offsetUnset($offset)
+	{
+		unset($this->data[$offset]);
+	}
+}
diff --git a/framework/db/Migration.php b/framework/db/Migration.php
index 0db728f..cd1db69 100644
--- a/framework/db/Migration.php
+++ b/framework/db/Migration.php
@@ -343,7 +343,6 @@ class Migration extends \yii\base\Component
 	 * Builds and executes a SQL statement for dropping a primary key.
 	 * @param string $name the name of the primary key constraint to be removed.
 	 * @param string $table the table that the primary key constraint will be removed from.
-	 * @return Command the command object itself
 	 */
 	public function dropPrimaryKey($name, $table)
 	{
diff --git a/framework/test/ActiveFixture.php b/framework/test/ActiveFixture.php
new file mode 100644
index 0000000..d330741
--- /dev/null
+++ b/framework/test/ActiveFixture.php
@@ -0,0 +1,161 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\test;
+
+use Yii;
+use yii\base\ArrayAccessTrait;
+use yii\base\InvalidConfigException;
+use yii\db\TableSchema;
+
+/**
+ * ActiveFixture represents a fixture backed up by a [[modelClass|ActiveRecord class]] or a [[tableName|database table]].
+ *
+ * Either [[modelClass]] or [[tableName]] must be set. And you should normally override [[loadSchema()]]
+ * to set up the necessary database schema (e.g. creating the table, view, trigger, etc.)
+ * You should also provide fixture data in the file specified by [[dataFile]] or overriding [[loadData()]] if you want
+ * to use code to generate the fixture data.
+ *
+ * When the fixture is being loaded, it will first call [[loadSchema()]] to initialize the database schema.
+ * It will then call [[loadData()]] to populate the table with the fixture data.
+ *
+ * After the fixture is loaded, you can access the loaded data via the [[data]] property. If you set [[modelClass]],
+ * you will also be able to retrieve an instance of [[modelClass]] with the populated data via [[getModel()]].
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class ActiveFixture extends BaseActiveFixture
+{
+	/**
+	 * @var string the name of the database table that this fixture is about. If this property is not set,
+	 * the table name will be determined via [[modelClass]].
+	 * @see modelClass
+	 */
+	public $tableName;
+	/**
+	 * @var string the file path or path alias of the data file that contains the fixture data
+	 * and will be loaded by [[loadData()]]. If this is not set, it will default to `FixturePath/data/TableName.php`,
+	 * where `FixturePath` stands for the directory containing this fixture class, and `TableName` stands for the
+	 * name of the table associated with this fixture.
+	 */
+	public $dataFile;
+	/**
+	 * @var boolean whether to reset the table associated with this fixture.
+	 * By setting this property to be true, when [[loadData()]] is called, all existing data in the table
+	 * will be removed and the sequence number (if any) will be reset.
+	 *
+	 * Note that you normally do not need to reset the table if you implement [[loadSchema()]] because
+	 * there will be no existing data.
+	 */
+	public $resetTable = false;
+	/**
+	 * @var TableSchema the table schema for the table associated with this fixture
+	 */
+	private $_table;
+
+	/**
+	 * @inheritdoc
+	 */
+	public function init()
+	{
+		parent::init();
+		if (!isset($this->modelClass) && !isset($this->tableName)) {
+			throw new InvalidConfigException('Either "modelClass" or "tableName" must be set.');
+		}
+	}
+
+	/**
+	 * @return TableSchema the schema information of the database table associated with this fixture.
+	 * @throws \yii\base\InvalidConfigException if the table does not exist
+	 */
+	public function getTableSchema()
+	{
+		if ($this->_table !== null) {
+			return $this->_table;
+		}
+
+		$db = $this->db;
+		$tableName = $this->tableName;
+		if ($tableName === null) {
+			/** @var \yii\db\ActiveRecord $modelClass */
+			$modelClass = $this->modelClass;
+			$tableName = $modelClass::tableName();
+		}
+
+		$this->_table = $db->getSchema()->getTableSchema($tableName);
+		if ($this->_table === null) {
+			throw new InvalidConfigException("Table does not exist: {$tableName}");
+		}
+
+		return $this->_table;
+	}
+
+	/**
+	 * Loads the fixture data.
+	 * The default implementation will first reset the DB table and then populate it with the data
+	 * returned by [[getData()]].
+	 */
+	protected function loadData()
+	{
+		$table = $this->getTableSchema();
+		if ($this->resetTable) {
+			$this->resetTable();
+		}
+		foreach ($this->getData() as $alias => $row) {
+			$this->db->createCommand()->insert($table->fullName, $row)->execute();
+			if ($table->sequenceName !== null) {
+				foreach ($table->primaryKey as $pk) {
+					if (!isset($row[$pk])) {
+						$row[$pk] = $this->db->getLastInsertID($table->sequenceName);
+						break;
+					}
+				}
+			}
+			$this->data[$alias] = $row;
+		}
+	}
+
+	/**
+	 * Returns the fixture data.
+	 * 
+	 * This method is called by [[loadData()]] to get the needed fixture data.
+	 *
+	 * The default implementation will try to return the fixture data by including the external file specified by [[dataFile]].
+	 * The file should return an array of data rows (column name => column value), each corresponding to a row in the table.
+	 *
+	 * If the data file does not exist, an empty array will be returned.
+	 *
+	 * @return array the data rows to be inserted into the database table.
+	 */
+	protected function getData()
+	{
+		if ($this->dataFile === false) {
+			return [];
+		}
+		if ($this->dataFile !== null) {
+			$dataFile = Yii::getAlias($this->dataFile);
+		} else {
+			$class = new \ReflectionClass($this);
+			$dataFile = dirname($class->getFileName()) . '/data/' . $this->getTableSchema()->fullName . '.php';
+		}
+		return is_file($dataFile) ? require($dataFile) : [];
+	}
+
+	/**
+	 * Removes all existing data from the specified table and resets sequence number if any.
+	 * This method is called before populating fixture data into the table associated with this fixture.
+	 */
+	protected function resetTable()
+	{
+		$table = $this->getTableSchema();
+		$this->db->createCommand()->delete($table->fullName)->execute();
+		if ($table->sequenceName !== null) {
+			$this->db->createCommand()->resetSequence($table->fullName, 1)->execute();
+		}
+	}
+}
diff --git a/framework/test/BaseActiveFixture.php b/framework/test/BaseActiveFixture.php
new file mode 100644
index 0000000..65293a8
--- /dev/null
+++ b/framework/test/BaseActiveFixture.php
@@ -0,0 +1,111 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\test;
+
+use Yii;
+use yii\base\ArrayAccessTrait;
+use yii\base\InvalidConfigException;
+
+/**
+ * BaseActiveFixture is the base class for fixture classes that support accessing fixture data as ActiveRecord objects.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+abstract class BaseActiveFixture extends DbFixture implements \IteratorAggregate, \ArrayAccess, \Countable
+{
+	use ArrayAccessTrait;
+
+	/**
+	 * @var string the AR model class associated with this fixture.
+	 * @see tableName
+	 */
+	public $modelClass;
+	/**
+	 * @var boolean whether to create the corresponding DB schema for this fixture.
+	 * By setting this property to be true, the [[loadSchema()]] method will be called when the fixture is loaded.
+	 */
+	public $loadSchema = true;
+	/**
+	 * @var boolean whether to load fixture data.
+	 * By setting this property to be true, the [[loadData()]] method will be called when the fixture is loaded.
+	 */
+	public $loadData = true;
+	/**
+	 * @var array the data rows. Each array element represents one row of data (column name => column value).
+	 */
+	public $data = [];
+	/**
+	 * @var \yii\db\ActiveRecord[] the loaded AR models
+	 */
+	private $_models = [];
+
+
+	/**
+	 * @inheritdoc
+	 */
+	public function load()
+	{
+		if ($this->loadSchema) {
+			$this->loadSchema();
+		}
+		if ($this->loadData) {
+			$this->loadData();
+		}
+	}
+
+	/**
+	 * Returns the AR model by the specified model name.
+	 * A model name is the key of the corresponding data row returned by [[loadData()]].
+	 * @param string $name the model name.
+	 * @return null|\yii\db\ActiveRecord the AR model, or null if the model cannot be found in the database
+	 * @throws \yii\base\InvalidConfigException if [[modelClass]] is not set.
+	 */
+	public function getModel($name)
+	{
+		if (!isset($this->data[$name])) {
+			return null;
+		}
+		if (array_key_exists($name, $this->_models)) {
+			return $this->_models[$name];
+		}
+
+		if ($this->modelClass === null) {
+			throw new InvalidConfigException('The "modelClass" property must be set.');
+		}
+		$row = $this->data[$name];
+		/** @var \yii\db\ActiveRecord $modelClass */
+		$modelClass = $this->modelClass;
+		/** @var \yii\db\ActiveRecord $model */
+		$model = new $modelClass;
+		$keys = [];
+		foreach ($model->primaryKey() as $key) {
+			$keys[$key] = isset($row[$key]) ? $row[$key] : null;
+		}
+		return $this->_models[$name] = $modelClass::find($keys);
+	}
+
+	/**
+	 * Creates the database schema needed by this fixture.
+	 * You may override this method by creating the DB table associated with this fixture
+	 * and other relevant DB elements, such as views, triggers.
+	 */
+	protected function loadSchema()
+	{
+	}
+
+	/**
+	 * Loads the fixture data.
+	 * The default implementation will first reset the DB table and then populate it with the data
+	 * returned by [[getData()]].
+	 */
+	protected function loadData()
+	{
+		return [];
+	}
+}
diff --git a/framework/test/DbFixture.php b/framework/test/DbFixture.php
new file mode 100644
index 0000000..f562d5f
--- /dev/null
+++ b/framework/test/DbFixture.php
@@ -0,0 +1,296 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\test;
+
+use Yii;
+use yii\base\InvalidConfigException;
+use yii\db\Connection;
+
+/**
+ * DbFixture is the base class for DB-related fixtures.
+ *
+ * DbFixture provides the [[db]] connection as well as a set of commonly used DB manipulation methods.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+abstract class DbFixture extends Fixture
+{
+	/**
+	 * @var Connection|string the DB connection object or the application component ID of the DB connection.
+	 * After the DbFixture object is created, if you want to change this property, you should only assign it
+	 * with a DB connection object.
+	 */
+	public $db = 'db';
+
+	/**
+	 * @inheritdoc
+	 */
+	public function init()
+	{
+		parent::init();
+		if (is_string($this->db)) {
+			$this->db = Yii::$app->getComponent($this->db);
+		}
+		if (!is_object($this->db)) {
+			throw new InvalidConfigException("The 'db' property must be either a DB connection instance or the application component ID of a DB connection.");
+		}
+	}
+
+	/**
+	 * Executes a SQL statement.
+	 * This method executes the specified SQL statement using [[db]].
+	 * @param string $sql the SQL statement to be executed
+	 * @param array $params input parameters (name => value) for the SQL execution.
+	 * See [[Command::execute()]] for more details.
+	 */
+	public function execute($sql, $params = [])
+	{
+		$this->db->createCommand($sql)->execute($params);
+	}
+
+	/**
+	 * Creates and executes an INSERT SQL statement.
+	 * The method will properly escape the column names, and bind the values to be inserted.
+	 * @param string $table the table that new rows will be inserted into.
+	 * @param array $columns the column data (name => value) to be inserted into the table.
+	 */
+	public function insert($table, $columns)
+	{
+		$this->db->createCommand()->insert($table, $columns)->execute();
+	}
+
+	/**
+	 * Creates and executes an batch INSERT SQL statement.
+	 * The method will properly escape the column names, and bind the values to be inserted.
+	 * @param string $table the table that new rows will be inserted into.
+	 * @param array $columns the column names.
+	 * @param array $rows the rows to be batch inserted into the table
+	 */
+	public function batchInsert($table, $columns, $rows)
+	{
+		$this->db->createCommand()->batchInsert($table, $columns, $rows)->execute();
+	}
+
+	/**
+	 * Creates and executes an UPDATE SQL statement.
+	 * The method will properly escape the column names and bind the values to be updated.
+	 * @param string $table the table to be updated.
+	 * @param array $columns the column data (name => value) to be updated.
+	 * @param array|string $condition the conditions that will be put in the WHERE part. Please
+	 * refer to [[Query::where()]] on how to specify conditions.
+	 * @param array $params the parameters to be bound to the query.
+	 */
+	public function update($table, $columns, $condition = '', $params = [])
+	{
+		$this->db->createCommand()->update($table, $columns, $condition, $params)->execute();
+	}
+
+	/**
+	 * Creates and executes a DELETE SQL statement.
+	 * @param string $table the table where the data will be deleted from.
+	 * @param array|string $condition the conditions that will be put in the WHERE part. Please
+	 * refer to [[Query::where()]] on how to specify conditions.
+	 * @param array $params the parameters to be bound to the query.
+	 */
+	public function delete($table, $condition = '', $params = [])
+	{
+		$this->db->createCommand()->delete($table, $condition, $params)->execute();
+	}
+
+	/**
+	 * Builds and executes a SQL statement for creating a new DB table.
+	 *
+	 * The columns in the new  table should be specified as name-definition pairs (e.g. 'name' => 'string'),
+	 * where name stands for a column name which will be properly quoted by the method, and definition
+	 * stands for the column type which can contain an abstract DB type.
+	 *
+	 * The [[QueryBuilder::getColumnType()]] method will be invoked to convert any abstract type into a physical one.
+	 *
+	 * If a column is specified with definition only (e.g. 'PRIMARY KEY (name, type)'), it will be directly
+	 * put into the generated SQL.
+	 *
+	 * @param string $table the name of the table to be created. The name will be properly quoted by the method.
+	 * @param array $columns the columns (name => definition) in the new table.
+	 * @param string $options additional SQL fragment that will be appended to the generated SQL.
+	 */
+	public function createTable($table, $columns, $options = null)
+	{
+		$this->db->createCommand()->createTable($table, $columns, $options)->execute();
+	}
+
+	/**
+	 * Builds and executes a SQL statement for renaming a DB table.
+	 * @param string $table the table to be renamed. The name will be properly quoted by the method.
+	 * @param string $newName the new table name. The name will be properly quoted by the method.
+	 */
+	public function renameTable($table, $newName)
+	{
+		$this->db->createCommand()->renameTable($table, $newName)->execute();
+	}
+
+	/**
+	 * Builds and executes a SQL statement for dropping a DB table.
+	 * @param string $table the table to be dropped. The name will be properly quoted by the method.
+	 */
+	public function dropTable($table)
+	{
+		$this->db->createCommand()->dropTable($table)->execute();
+	}
+
+	/**
+	 * Builds and executes a SQL statement for truncating a DB table.
+	 * @param string $table the table to be truncated. The name will be properly quoted by the method.
+	 */
+	public function truncateTable($table)
+	{
+		$this->db->createCommand()->truncateTable($table)->execute();
+	}
+
+	/**
+	 * Builds and executes a SQL statement for adding a new DB column.
+	 * @param string $table the table that the new column will be added to. The table name will be properly quoted by the method.
+	 * @param string $column the name of the new column. The name will be properly quoted by the method.
+	 * @param string $type the column type. The [[QueryBuilder::getColumnType()]] method will be invoked to convert abstract column type (if any)
+	 * into the physical one. Anything that is not recognized as abstract type will be kept in the generated SQL.
+	 * For example, 'string' will be turned into 'varchar(255)', while 'string not null' will become 'varchar(255) not null'.
+	 */
+	public function addColumn($table, $column, $type)
+	{
+		$this->db->createCommand()->addColumn($table, $column, $type)->execute();
+	}
+
+	/**
+	 * Builds and executes a SQL statement for dropping a DB column.
+	 * @param string $table the table whose column is to be dropped. The name will be properly quoted by the method.
+	 * @param string $column the name of the column to be dropped. The name will be properly quoted by the method.
+	 */
+	public function dropColumn($table, $column)
+	{
+		$this->db->createCommand()->dropColumn($table, $column)->execute();
+	}
+
+	/**
+	 * Builds and executes a SQL statement for renaming a column.
+	 * @param string $table the table whose column is to be renamed. The name will be properly quoted by the method.
+	 * @param string $name the old name of the column. The name will be properly quoted by the method.
+	 * @param string $newName the new name of the column. The name will be properly quoted by the method.
+	 */
+	public function renameColumn($table, $name, $newName)
+	{
+		$this->db->createCommand()->renameColumn($table, $name, $newName)->execute();
+	}
+
+	/**
+	 * Builds and executes a SQL statement for changing the definition of a column.
+	 * @param string $table the table whose column is to be changed. The table name will be properly quoted by the method.
+	 * @param string $column the name of the column to be changed. The name will be properly quoted by the method.
+	 * @param string $type the new column type. The [[getColumnType()]] method will be invoked to convert abstract column type (if any)
+	 * into the physical one. Anything that is not recognized as abstract type will be kept in the generated SQL.
+	 * For example, 'string' will be turned into 'varchar(255)', while 'string not null' will become 'varchar(255) not null'.
+	 */
+	public function alterColumn($table, $column, $type)
+	{
+		$this->db->createCommand()->alterColumn($table, $column, $type)->execute();
+	}
+
+	/**
+	 * Builds and executes a SQL statement for creating a primary key.
+	 * The method will properly quote the table and column names.
+	 * @param string $name the name of the primary key constraint.
+	 * @param string $table the table that the primary key constraint will be added to.
+	 * @param string|array $columns comma separated string or array of columns that the primary key will consist of.
+	 */
+	public function addPrimaryKey($name, $table, $columns)
+	{
+		$this->db->createCommand()->addPrimaryKey($name, $table, $columns)->execute();
+	}
+
+	/**
+	 * Builds and executes a SQL statement for dropping a primary key.
+	 * @param string $name the name of the primary key constraint to be removed.
+	 * @param string $table the table that the primary key constraint will be removed from.
+	 */
+	public function dropPrimaryKey($name, $table)
+	{
+		$this->db->createCommand()->dropPrimaryKey($name, $table)->execute();
+	}
+
+	/**
+	 * Builds a SQL statement for adding a foreign key constraint to an existing table.
+	 * The method will properly quote the table and column names.
+	 * @param string $name the name of the foreign key constraint.
+	 * @param string $table the table that the foreign key constraint will be added to.
+	 * @param string $columns the name of the column to that the constraint will be added on. If there are multiple columns, separate them with commas.
+	 * @param string $refTable the table that the foreign key references to.
+	 * @param string $refColumns the name of the column that the foreign key references to. If there are multiple columns, separate them with commas.
+	 * @param string $delete the ON DELETE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL
+	 * @param string $update the ON UPDATE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL
+	 */
+	public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete = null, $update = null)
+	{
+		$this->db->createCommand()->addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete, $update)->execute();
+	}
+
+	/**
+	 * Builds a SQL statement for dropping a foreign key constraint.
+	 * @param string $name the name of the foreign key constraint to be dropped. The name will be properly quoted by the method.
+	 * @param string $table the table whose foreign is to be dropped. The name will be properly quoted by the method.
+	 */
+	public function dropForeignKey($name, $table)
+	{
+		$this->db->createCommand()->dropForeignKey($name, $table)->execute();
+	}
+
+	/**
+	 * Builds and executes a SQL statement for creating a new index.
+	 * @param string $name the name of the index. The name will be properly quoted by the method.
+	 * @param string $table the table that the new index will be created for. The table name will be properly quoted by the method.
+	 * @param string $column the column(s) that should be included in the index. If there are multiple columns, please separate them
+	 * by commas. The column names will be properly quoted by the method.
+	 * @param boolean $unique whether to add UNIQUE constraint on the created index.
+	 */
+	public function createIndex($name, $table, $column, $unique = false)
+	{
+		$this->db->createCommand()->createIndex($name, $table, $column, $unique)->execute();
+	}
+
+	/**
+	 * Builds and executes a SQL statement for dropping an index.
+	 * @param string $name the name of the index to be dropped. The name will be properly quoted by the method.
+	 * @param string $table the table whose index is to be dropped. The name will be properly quoted by the method.
+	 */
+	public function dropIndex($name, $table)
+	{
+		$this->db->createCommand()->dropIndex($name, $table)->execute();
+	}
+	/**
+	 * Creates and executes a SQL command to reset the sequence value of a table's primary key.
+	 * The sequence will be reset such that the primary key of the next new row inserted
+	 * will have the specified value or 1.
+	 * @param string $table the name of the table whose primary key sequence will be reset
+	 * @param mixed $value the value for the primary key of the next new row inserted. If this is not set,
+	 * the next new row's primary key will have a value 1.
+	 */
+	public function resetSequence($table, $value = null)
+	{
+		$this->db->createCommand()->resetSequence($table, $value)->execute();
+	}
+
+	/**
+	 * Builds and executes a SQL command for enabling or disabling integrity check.
+	 * @param boolean $check whether to turn on or off the integrity check.
+	 * @param string $schema the schema name of the tables. Defaults to empty string, meaning the current
+	 * or default schema.
+	 * @param string $table the table name.
+	 */
+	public function checkIntegrity($check = true, $schema = '', $table = '')
+	{
+		$this->db->createCommand()->checkIntegrity($check, $schema, $table)->execute();
+	}
+}
diff --git a/framework/test/Fixture.php b/framework/test/Fixture.php
new file mode 100644
index 0000000..8ec0bbd
--- /dev/null
+++ b/framework/test/Fixture.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\test;
+
+use yii\base\Component;
+
+/**
+ * Fixture represents a fixed state of a test environment.
+ *
+ * Each fixture instance represents a particular aspect of a test environment. For example,
+ * you may use `UserFixture` to initialize the user database table with a set of known data. You may
+ * load the fixture when running every test method so that the user table always contains the fixed data
+ * and thus allows your test predictable and repeatable.
+ *
+ * A fixture may depend on other fixtures, specified via the [[depends]] property. When a fixture is being loaded,
+ * its dependent fixtures will be automatically loaded BEFORE the fixture; and when the fixture is being unloaded,
+ * its dependent fixtures will be unloaded AFTER the fixture.
+ *
+ * You should normally override [[load()]] to specify how to set up a fixture; and override [[unload()]]
+ * for clearing up a fixture.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class Fixture extends Component
+{
+	/**
+	 * @var array the fixtures that this fixture depends on. This must be a list of the dependent
+	 * fixture class names.
+	 */
+	public $depends = [];
+
+	/**
+	 * Loads the fixture.
+	 * This method is called before performing every test method.
+	 * You should override this method with concrete implementation about how to set up the fixture.
+	 */
+	public function load()
+	{
+	}
+
+	/**
+	 * This method is called BEFORE any fixture data is loaded for the current test.
+	 */
+	public function beforeLoad()
+	{
+	}
+
+	/**
+	 * This method is called AFTER all fixture data have been loaded for the current test.
+	 */
+	public function afterLoad()
+	{
+	}
+
+	/**
+	 * Unloads the fixture.
+	 * This method is called after every test method finishes.
+	 * You may override this method to perform necessary cleanup work for the fixture.
+	 */
+	public function unload()
+	{
+	}
+}
+
diff --git a/framework/test/FixtureTrait.php b/framework/test/FixtureTrait.php
new file mode 100644
index 0000000..78bc71d
--- /dev/null
+++ b/framework/test/FixtureTrait.php
@@ -0,0 +1,211 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\test;
+
+use Yii;
+use yii\base\InvalidConfigException;
+use yii\base\UnknownMethodException;
+use yii\base\UnknownPropertyException;
+
+/**
+ * FixtureTrait provides functionalities for loading, unloading and accessing fixtures for a test case.
+ *
+ * By using FixtureTrait, a test class will be able to specify which fixtures to load by overriding
+ * the [[fixtures()]] method. It can then load and unload the fixtures using [[loadFixtures()]] and [[unloadFixtures()]].
+ * Once a fixture is loaded, it can be accessed like an object property, thanks to the PHP `__get()` magic method.
+ * Also, if the fixture is an instance of [[ActiveFixture]], you will be able to access AR models
+ * through the syntax `$this->fixtureName('model name')`.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+trait FixtureTrait
+{
+	/**
+	 * @var array the list of fixture objects available for the current test.
+	 * The array keys are the corresponding fixture class names.
+	 * The fixtures are listed in their dependency order. That is, fixture A is listed before B
+	 * if B depends on A.
+	 */
+	private $_fixtures;
+	/**
+	 * @var array the fixture class names indexed by the corresponding fixture names (aliases).
+	 */
+	private $_fixtureAliases;
+
+	/**
+	 * Returns the value of an object property.
+	 *
+	 * Do not call this method directly as it is a PHP magic method that
+	 * will be implicitly called when executing `$value = $object->property;`.
+	 * @param string $name the property name
+	 * @return mixed the property value
+	 * @throws UnknownPropertyException if the property is not defined
+	 */
+	public function __get($name)
+	{
+		$fixture = $this->getFixture($name);
+		if ($fixture !== null) {
+			return $fixture;
+		} else {
+			throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '::' . $name);
+		}
+	}
+
+	/**
+	 * Calls the named method which is not a class method.
+	 *
+	 * Do not call this method directly as it is a PHP magic method that
+	 * will be implicitly called when an unknown method is being invoked.
+	 * @param string $name the method name
+	 * @param array $params method parameters
+	 * @throws UnknownMethodException when calling unknown method
+	 * @return mixed the method return value
+	 */
+	public function __call($name, $params)
+	{
+		$fixture = $this->getFixture($name);
+		if ($fixture instanceof ActiveFixture) {
+			return $fixture->getModel(reset($params));
+		} else {
+			throw new UnknownMethodException('Unknown method: ' . get_class($this) . "::$name()");
+		}
+	}
+
+	/**
+	 * Declares the fixtures that are needed by the current test case.
+	 * The return value of this method must be an array of fixture configurations. For example,
+	 *
+	 * ```php
+	 * [
+	 *     // anonymous fixture
+	 *     PostFixture::className(),
+	 *     // "users" fixture
+	 *     'users' => UserFixture::className(),
+	 *     // "cache" fixture with configuration
+	 *     'cache' => [
+	 *          'class' => CacheFixture::className(),
+	 *          'host' => 'xxx',
+	 *     ],
+	 * ]
+	 * ```
+	 *
+	 * Note that the actual fixtures used for a test case will include both [[globalFixtures()]]
+	 * and [[fixtures()]].
+	 *
+	 * @return array the fixtures needed by the current test case
+	 */
+	protected function fixtures()
+	{
+		return [];
+	}
+
+	/**
+	 * Declares the fixtures shared required by different test cases.
+	 * The return value should be similar to that of [[fixtures()]].
+	 * You should usually override this method in a base class.
+	 * @return array the fixtures shared and required by different test cases.
+	 * @see fixtures()
+	 */
+	protected function globalFixtures()
+	{
+		return [];
+	}
+
+	/**
+	 * Loads the fixtures.
+	 * This method will load the fixtures specified by `$fixtures` or [[globalFixtures()]] and [[fixtures()]].
+	 * @param array $fixtures the fixtures to loaded. If not set, [[fixtures()]] will be loaded instead.
+	 * @throws InvalidConfigException if fixtures are not properly configured or if a circular dependency among
+	 * the fixtures is detected.
+	 */
+	protected function loadFixtures($fixtures = null)
+	{
+		if ($fixtures === null) {
+			$fixtures = array_merge($this->globalFixtures(), $this->fixtures());
+		}
+
+		// normalize fixture configurations
+		$config = [];  // configuration provided in test case
+		$this->_fixtureAliases = [];
+		foreach ($fixtures as $name => $fixture) {
+			if (!is_array($fixture)) {
+				$fixtures[$name] = $fixture = ['class' => $fixture];
+			} elseif (!isset($fixture['class'])) {
+				throw new InvalidConfigException("You must specify 'class' for the fixture '$name'.");
+			}
+			$config[$fixture['class']] = $fixture;
+			$this->_fixtureAliases[$name] = $fixture['class'];
+		}
+
+		// create fixture instances
+		$this->_fixtures = [];
+		$stack = array_reverse($fixtures);
+		while (($fixture = array_pop($stack)) !== null) {
+			if ($fixture instanceof Fixture) {
+				$class = get_class($fixture);
+				unset($this->_fixtures[$class]);  // unset so that the fixture is added to the last in the next line
+				$this->_fixtures[$class] = $fixture;
+			} else {
+				$class = $fixture['class'];
+				if (!isset($this->_fixtures[$class])) {
+					$this->_fixtures[$class] = false;
+					$stack[] = $fixture = Yii::createObject($fixture);
+					foreach ($fixture->depends as $dep) {
+						// need to use the configuration provided in test case
+						$stack[] = isset($config[$dep]) ? $config[$dep] : ['class' => $dep];
+	 				}
+				} elseif ($this->_fixtures[$class] === false) {
+					throw new InvalidConfigException("A circular dependency is detected for fixture '$class'.");
+				}
+			}
+		}
+
+		// load fixtures
+		/** @var Fixture $fixture */
+		foreach ($this->_fixtures as $fixture) {
+			$fixture->beforeLoad();
+		}
+		foreach ($this->_fixtures as $fixture) {
+			$fixture->load();
+		}
+		foreach ($this->_fixtures as $fixture) {
+			$fixture->afterLoad();
+		}
+	}
+
+	/**
+	 * Unloads all existing fixtures.
+	 */
+	protected function unloadFixtures()
+	{
+		/** @var Fixture $fixture */
+		foreach (array_reverse($this->_fixtures) as $fixture) {
+			$fixture->unload();
+		}
+	}
+
+	/**
+	 * @return array the loaded fixtures for the current test case
+	 */
+	protected function getFixtures()
+	{
+		return $this->_fixtures;
+	}
+
+	/**
+	 * Returns the named fixture.
+	 * @param string $name the fixture alias or class name
+	 * @return Fixture the fixture object, or null if the named fixture does not exist.
+	 */
+	protected function getFixture($name)
+	{
+		$class = isset($this->_fixtureAliases[$name]) ? $this->_fixtureAliases[$name] : $name;
+		return isset($this->_fixtures[$class]) ? $this->_fixtures[$class] : null;
+	}
+}
diff --git a/framework/test/InitDbFixture.php b/framework/test/InitDbFixture.php
new file mode 100644
index 0000000..9a4ddf9
--- /dev/null
+++ b/framework/test/InitDbFixture.php
@@ -0,0 +1,74 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\test;
+
+use Yii;
+
+/**
+ * InitDbFixture represents the initial state needed for DB-related tests.
+ *
+ * Its main task is to toggle integrity check of the database during data loading.
+ * This is needed by other DB-related fixtures (e.g. [[ActiveFixture]]) so that they can populate
+ * data into the database without triggering integrity check errors.
+ *
+ * Besides, DbFixture also attempts to load an [[initScript|initialization script]] if it exists.
+ *
+ * You should normally use InitDbFixture to prepare a skeleton test database.
+ * Other DB fixtures will then add specific tables and data to this database.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class InitDbFixture extends DbFixture
+{
+	/**
+	 * @var string the init script file that should be executed when loading this fixture.
+	 * This should be either a file path or path alias. Note that if the file does not exist,
+	 * no error will be raised.
+	 */
+	public $initScript = '@app/tests/fixtures/initdb.php';
+	/**
+	 * @var array list of database schemas that the test tables may reside in. Defaults to
+	 * [''], meaning using the default schema (an empty string refers to the
+	 * default schema). This property is mainly used when turning on and off integrity checks
+	 * so that fixture data can be populated into the database without causing problem.
+	 */
+	public $schemas = [''];
+
+
+	/**
+	 * @inheritdoc
+	 */
+	public function beforeLoad()
+	{
+		foreach ($this->schemas as $schema) {
+			$this->checkIntegrity(false, $schema);
+		}
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function afterLoad()
+	{
+		foreach ($this->schemas as $schema) {
+			$this->checkIntegrity(true, $schema);
+		}
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function load()
+	{
+		$file = Yii::getAlias($this->initScript);
+		if (is_file($file)) {
+			require($file);
+		}
+	}
+}
diff --git a/tests/unit/framework/test/ActiveFixtureTest.php b/tests/unit/framework/test/ActiveFixtureTest.php
new file mode 100644
index 0000000..bea0361
--- /dev/null
+++ b/tests/unit/framework/test/ActiveFixtureTest.php
@@ -0,0 +1,116 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yiiunit\framework\test;
+
+use yii\test\ActiveFixture;
+use yii\test\FixtureTrait;
+use yii\test\InitDbFixture;
+use yiiunit\data\ar\ActiveRecord;
+use yiiunit\framework\db\DatabaseTestCase;
+
+class Customer extends ActiveRecord
+{
+	public static function tableName()
+	{
+		return 'tbl_customer2';
+	}
+}
+
+class CustomerFixture extends ActiveFixture
+{
+	public $modelClass = 'yiiunit\framework\test\Customer';
+
+	protected function loadSchema()
+	{
+		try {
+			$this->dropTable('tbl_customer2');
+		} catch (\Exception $e) {
+		}
+		$this->createTable('tbl_customer2', [
+			'id' => 'pk',
+			'email' => 'string',
+			'name' => 'string',
+			'address' => 'string',
+			'status' => 'integer',
+		]);
+	}
+}
+
+class MyDbTestCase
+{
+	use FixtureTrait;
+
+	public function setUp()
+	{
+		$this->loadFixtures();
+	}
+
+	public function tearDown()
+	{
+		$this->unloadFixtures();
+	}
+
+	protected function fixtures()
+	{
+		return [
+			'customers' => CustomerFixture::className(),
+		];
+	}
+
+	protected function globalFixtures()
+	{
+		return [
+			InitDbFixture::className(),
+		];
+	}
+}
+
+/**
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class ActiveFixtureTest extends DatabaseTestCase
+{
+	public function setUp()
+	{
+		parent::setUp();
+		\Yii::$app->setComponent('db', $this->getConnection());
+		Customer::$db = $this->getConnection();
+	}
+
+	public function tearDown()
+	{
+		parent::tearDown();
+	}
+
+	public function testGetData()
+	{
+		$test = new MyDbTestCase();
+		$test->setUp();
+		$fixture = $test->customers;
+		$this->assertEquals(CustomerFixture::className(), get_class($fixture));
+		$this->assertEquals(2, count($fixture));
+		$this->assertEquals(1, $fixture['customer1']['id']);
+		$this->assertEquals('customer1@example.com', $fixture['customer1']['email']);
+		$this->assertEquals(2, $fixture['customer2']['id']);
+		$this->assertEquals('customer2@example.com', $fixture['customer2']['email']);
+	}
+
+	public function testGetModel()
+	{
+		$test = new MyDbTestCase();
+		$test->setUp();
+		$fixture = $test->customers;
+		$this->assertEquals(Customer::className(), get_class($fixture->getModel('customer1')));
+		$this->assertEquals(1, $fixture->getModel('customer1')->id);
+		$this->assertEquals('customer1@example.com', $fixture->getModel('customer1')->email);
+		$this->assertEquals(2, $fixture->getModel('customer2')->id);
+		$this->assertEquals('customer2@example.com', $fixture->getModel('customer2')->email);
+	}
+}
diff --git a/tests/unit/framework/test/FixtureTest.php b/tests/unit/framework/test/FixtureTest.php
new file mode 100644
index 0000000..c621a2f
--- /dev/null
+++ b/tests/unit/framework/test/FixtureTest.php
@@ -0,0 +1,171 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yiiunit\framework\test;
+
+use yii\test\Fixture;
+use yii\test\FixtureTrait;
+use yiiunit\TestCase;
+
+class Fixture1 extends Fixture
+{
+	public $depends = ['yiiunit\framework\test\Fixture2'];
+
+	public function load()
+	{
+		MyTestCase::$load .= '1';
+	}
+
+	public function unload()
+	{
+		MyTestCase::$unload .= '1';
+	}
+}
+
+class Fixture2 extends Fixture
+{
+	public $depends = ['yiiunit\framework\test\Fixture3'];
+	public function load()
+	{
+		MyTestCase::$load .= '2';
+	}
+
+
+	public function unload()
+	{
+		MyTestCase::$unload .= '2';
+	}
+}
+
+class Fixture3 extends Fixture
+{
+	public function load()
+	{
+		MyTestCase::$load .= '3';
+	}
+
+
+	public function unload()
+	{
+		MyTestCase::$unload .= '3';
+	}
+}
+
+class MyTestCase
+{
+	use FixtureTrait;
+
+	public $scenario = 1;
+	public static $load;
+	public static $unload;
+
+	public function setUp()
+	{
+		$this->loadFixtures();
+	}
+
+	public function tearDown()
+	{
+		$this->unloadFixtures();
+	}
+
+	public function fetchFixture($name)
+	{
+		return $this->getFixture($name);
+	}
+
+	protected function fixtures()
+	{
+		switch ($this->scenario) {
+			case 0: return [];
+			case 1: return [
+				'fixture1' => Fixture1::className(),
+			];
+			case 2: return [
+				'fixture2' => Fixture2::className(),
+			];
+			case 3: return [
+				'fixture3' => Fixture3::className(),
+			];
+			case 4: return [
+				'fixture1' => Fixture1::className(),
+				'fixture2' => Fixture2::className(),
+			];
+			case 5: return [
+				'fixture2' => Fixture2::className(),
+				'fixture3' => Fixture3::className(),
+			];
+			case 6: return [
+				'fixture1' => Fixture1::className(),
+				'fixture3' => Fixture3::className(),
+			];
+			case 7:
+			default: return [
+				'fixture1' => Fixture1::className(),
+				'fixture2' => Fixture2::className(),
+				'fixture3' => Fixture3::className(),
+			];
+		}
+	}
+}
+
+class FixtureTest extends TestCase
+{
+	public function testDependencies()
+	{
+		foreach ($this->getDependencyTests() as $scenario => $result) {
+			$test = new MyTestCase();
+			$test->scenario = $scenario;
+			$test->setUp();
+			foreach ($result as $name => $loaded) {
+				$this->assertEquals($loaded, $test->fetchFixture($name) !== null, "Verifying scenario $scenario fixture $name");
+			}
+		}
+	}
+
+	public function testLoadSequence()
+	{
+		foreach ($this->getLoadSequenceTests() as $scenario => $result) {
+			$test = new MyTestCase();
+			$test->scenario = $scenario;
+			MyTestCase::$load = '';
+			MyTestCase::$unload = '';
+			$test->setUp();
+			$this->assertEquals($result[0], MyTestCase::$load, "Verifying scenario $scenario load sequence");
+			$test->tearDown();
+			$this->assertEquals($result[1], MyTestCase::$unload, "Verifying scenario $scenario unload sequence");
+		}
+	}
+
+	protected function getDependencyTests()
+	{
+		return [
+			0 => ['fixture1' => false, 'fixture2' => false, 'fixture3' => false],
+			1 => ['fixture1' => true, 'fixture2' => false, 'fixture3' => false],
+			2 => ['fixture1' => false, 'fixture2' => true, 'fixture3' => false],
+			3 => ['fixture1' => false, 'fixture2' => false, 'fixture3' => true],
+			4 => ['fixture1' => true, 'fixture2' => true, 'fixture3' => false],
+			5 => ['fixture1' => false, 'fixture2' => true, 'fixture3' => true],
+			6 => ['fixture1' => true, 'fixture2' => false, 'fixture3' => true],
+			7 => ['fixture1' => true, 'fixture2' => true, 'fixture3' => true],
+		];
+	}
+
+	protected function getLoadSequenceTests()
+	{
+		return [
+			0 => ['', ''],
+			1 => ['321', '123'],
+			2 => ['32', '23'],
+			3 => ['3', '3'],
+			4 => ['321', '123'],
+			5 => ['32', '23'],
+			6 => ['321', '123'],
+			7 => ['321', '123'],
+		];
+	}
+}
diff --git a/tests/unit/framework/test/data/tbl_customer2.php b/tests/unit/framework/test/data/tbl_customer2.php
new file mode 100644
index 0000000..a1981d7
--- /dev/null
+++ b/tests/unit/framework/test/data/tbl_customer2.php
@@ -0,0 +1,16 @@
+<?php
+
+return [
+	'customer1' => [
+		'email' => 'customer1@example.com',
+		'name' => 'customer1',
+		'address' => 'address1',
+		'status' => 1,
+	],
+	'customer2' => [
+		'email' => 'customer2@example.com',
+		'name' => 'customer2',
+		'address' => 'address2',
+		'status' => 2,
+	],
+];