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 initialize the user database table with a set of 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]], make sure you 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 setting up the test data needed by a database table. You may specify the table
by setting either the [[yii\test\ActiveFixture::tableName]] property or the [[yii\test\ActiveFixture::modelClass]]
property. The latter takes the name of an `ActiveRecord` class whose associated table will be used by the fixture.

```php
namespace app\tests\fixtures;

use yii\test\ActiveFixture;

class UserFixture extends ActiveFixture
{
	public $modelClass = 'app\models\User';
}
```

Next, you should provide the data needed by the user table in a file. By default, the file should be 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`, assuming the table associated with `User` is `tbl_user`.

The data file should return an array of data rows to be inserted into the user table. For example,

```php
<?php
return [
	'user1' => [
		'name' => 'Chase',
		'login' => 'lmayert',
		'email' => 'strosin.vernice@jerde.com',
		'auth_key' => 'K3nF70it7tzNsHddEiq0BZ0i-OU8S3xV',
		'password' => '$2y$13$WSyE5hHsG1rWN2jV8LRHzubilrCLI5Ev/iK0r3jRuwQEs2ldRu.a2',
	],
	'user2' => [
		'name' => 'Celestine',
		'login' => '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.

> Info: You may customize the location of the data file by setting the [[yii\test\ActiveFixture::dataFile]] property.
> If you set this property to be false, or if you do not provide the data file, the fixture will not load any data
> into the user table.

As we described earlier, a fixture may depend on other fixtures. For example, `UserProfileFixture` depends on `UserFixture`.
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 = ['yii\test\DbFixture', 'app\tests\fixtures\UserFixture'];
}
```

Note that in the above, besides `app\tests\fixtures\UserFixture`, the dependency of `UserProfileFixture` also includes
`yii\test\DbFixture`. This is required by all `ActiveFixture` classes which set `yii\test\DbFixture` as the default value
of the `depends` property. The `DbFixture` class is responsible for toggling database integrity check and executing
an initialization script. Without `DbFixture`, you may not be able to freely inserting or deleting rows in a table
due to various DB constraints.


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\TestCase]], 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\TestCase;
use app\tests\fixtures\UserProfileFixture;

class UserProfileTest extends TestCase
{
	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` and `DbFixture`, when running any test method in the test class,
three fixtures will be loaded sequentially: `DbFixture`, `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.
