Fixtures
========

Los fixtures son una parte importante de los tests. Su propósito principal es el de preparar el entorno en una estado fijado/conocido
de manera que los tests sean repetibles y corran de la manera esperada. Yii provee un framework de fixtures que te permite
dichos fixtures de manera precisa y usarlo de forma simple.

Un concepto clave en el framework de fixtures de Yii es el llamado *objeto fixture*. Un objeto fixture representa
un aspecto particular de un entorno de pruebas y es una instancia de [[yii\test\Fixture]] o heredada de esta. Por ejemplo,
puedes utilizar `UserFixture` para asegurarte de que la tabla de usuarios de la BD contiene un grupo de datos fijos. Entonces cargas uno o varios
objetos fixture antes de correr un test y lo descargas cuando el test ha concluido.

Un fixture puede depender de otros fixtures, especificándolo en su propiedad [[yii\test\Fixture::depends]].
Cuando un fixture está siendo cargado, los fixtures de los que depende serán cargados automáticamente ANTES que él;
y cuando el fixture está siendo descargado, los fixtures dependientes serán descargados DESPUÉS de él.


Definir un Fixture
------------------

Para definir un fixture, crea una nueva clase que extienda de [[yii\test\Fixture]] o [[yii\test\ActiveFixture]].
El primero es más adecuado para fixtures de propósito general, mientras que el último tiene características mejoradas específicamente
diseñadas para trabajar con base de datos y ActiveRecord.

El siguiente código define un fixture acerca del ActiveRecord `User` y su correspondiente tabla user.

```php
<?php
namespace app\tests\fixtures;

use yii\test\ActiveFixture;

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

> Tip: Cada `ActiveFixture` se encarga de preparar la tabla de la DB para los tests. Puedes especificar la tabla
> definiendo tanto la propiedad [[yii\test\ActiveFixture::tableName]] o la propiedad [[yii\test\ActiveFixture::modelClass]].
> Haciéndolo como el último, el nombre de la tabla será tomado de la clase `ActiveRecord` especificada en `modelClass`.

> Note: [[yii\test\ActiveFixture]] es sólo adecualdo para bases de datos SQL. Para bases de datos NoSQL, Yii provee
> las siguientes clases `ActiveFixture`:
>
> - Mongo DB: [[yii\mongodb\ActiveFixture]]
> - Elasticsearch: [[yii\elasticsearch\ActiveFixture]] (desde la versión 2.0.2)


Los datos para un fixture `ActiveFixture` son usualmente provistos en un archivo ubicado en `FixturePath/data/TableName.php`,
donde `FixturePath` corresponde al directorio conteniendo el archivo de clase del fixture, y `TableName`
es el nombre de la tabla asociada al fixture. En el ejemplo anterior, el archivo debería ser
`@app/tests/fixtures/data/user.php`. El archivo de datos debe devolver un array de registros
a ser insertados en la tabla user. Por ejemplo,

```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',
    ],
];
```

Puedes dar un alias al registro tal que más tarde en tu test, puedas referirte a ese registra a través de dicho alias. En el ejemplo anterior,
los dos registros tienen como alias `user1` y `user2`, respectivamente.

Además, no necesitas especificar los datos de columnas auto-incrementales. Yii automáticamente llenará esos valores
dentro de los registros cuando el fixture está siendo cargado.

> Tip: Puedes personalizar la ubicación del archivo de datos definiendo la propiedad [[yii\test\ActiveFixture::dataFile]].
> Puedes también sobrescribir [[yii\test\ActiveFixture::getData()]] para obtener los datos.

Como se describió anteriormente, un fixture puede depender de otros fixtures. Por ejemplo, un `UserProfileFixture` puede necesitar depender de `UserFixture`
porque la table de perfiles de usuarios contiene una clave foránea a la tabla user.
La dependencia es especificada vía la propiedad [[yii\test\Fixture::depends]], como a continuación,

```php
namespace app\tests\fixtures;

use yii\test\ActiveFixture;

class UserProfileFixture extends ActiveFixture
{
    public $modelClass = 'app\models\UserProfile';
    public $depends = ['app\tests\fixtures\UserFixture'];
}
```

La dependencia también asegura que los fixtures son cargados y descargados en un orden bien definido. En el ejemplo `UserFixture`
será siempre cargado antes de `UserProfileFixture` para asegurar que todas las referencias de las claves foráneas existan y será siempre descargado después de `UserProfileFixture`
por la misma razón.

Arriba te mostramos cómo definir un fixture de BD. Para definir un fixture no relacionado a BD
(por ej. un fixture acerca de archivos y directorios), puedes extender de la clase base más general
[[yii\test\Fixture]] y sobrescribir los métodos [[yii\test\Fixture::load()|load()]] y [[yii\test\Fixture::unload()|unload()]].


Utilizar Fixtures
-----------------

Si estás utilizando [Codeception](http://codeception.com/) para hacer tests de tu código, deberías considerar el utilizar
la extensión `yii2-codeception`, que tiene soporte incorporado para la carga y acceso a fixtures.
En caso de que utilices otros frameworks de testing, puedes usar [[yii\test\FixtureTrait]] en tus casos de tests
para alcanzar el mismo objetivo.

A continuación describiremos cómo escribir una clase de test de unidad `UserProfile` utilizando `yii2-codeception`.

En tu clase de test de unidad que extiende de [[yii\codeception\DbTestCase]] o [[yii\codeception\TestCase]],
indica cuáles fixtures quieres utilizar en el método [[yii\test\FixtureTrait::fixtures()|fixtures()]]. Por ejemplo,

```php
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(),
        ];
    }

    // ...métodos de test...
}
```

Los fixtures listados en el método `fixtures()` serán automáticamente cargados antes de correr cada método de test
en el caso de test y descargado al finalizar cada uno. También, como describimos antes, cuando un fixture está
siendo cargado, todos sus fixtures dependientes serán cargados primero. En el ejemplo de arriba, debido a que
`UserProfileFixture` depende de `UserFixture`, cuando ejecutas cualquier método de test en la clase,
dos fixtures serán cargados secuencialmente: `UserFixture` y `UserProfileFixture`.

Al especificar fixtures en `fixtures()`, puedes utilizar tanto un nombre de clase o un array de configuración para referirte a
un fixture. El array de configuración te permitirá personalizar las propiedades del fixture cuando este es cargado.

Puedes también asignarles alias a los fixtures. En el ejemplo anterior, el `UserProfileFixture` tiene como alias `profiles`.
En los métodos de test, puedes acceder a un objeto fixture utilizando su alias. Por ejemplo, `$this->profiles`
devolverá el objeto `UserProfileFixture`.

Dado que `UserProfileFixture` extiende de `ActiveFixture`, puedes por lo tanto usar la siguiente sintáxis para acceder
a los datos provistos por el fixture:

```php
// devuelve el registro del fixture cuyo alias es 'user1'
$row = $this->profiles['user1'];
// devuelve el modelo UserProfile correspondiente al registro cuyo alias es 'user1'
$profile = $this->profiles('user1');
// recorre cada registro en el fixture
foreach ($this->profiles as $row) ...
```

> Info: `$this->profiles` es todavía del tipo `UserProfileFixture`. Las características de acceso mostradas arriba son implementadas
> a través de métodos mágicos de PHP.


Definir y Utilizar Fixtures Globales
------------------------------------

Los fixtures descritos arriba son principalmente utilizados para casos de tests individuales. En la mayoría de los casos, puedes necesitar algunos
fixtures globales que sean aplicados a TODOS o muchos casos de test. Un ejemplo sería [[yii\test\InitDbFixture]], que hace
dos cosas:

* Realiza alguna tarea de inicialización común al ejectutar un script ubicado en `@app/tests/fixtures/initdb.php`;
* Deshabilita la comprobación de integridad antes de cargar otros fixtures de BD, y la rehabilita después de que todos los fixtures son descargados.

Utilizar fixtures globales es similar a utilizar los no-globales. La única diferencia es que declaras estos fixtures
en [[yii\codeception\TestCase::globalFixtures()]] en vez de en `fixtures()`. Cuando un caso de test carga fixtures,
primero carga los globales y luego los no-globales.

Por defecto, [[yii\codeception\DbTestCase]] ya declara `InitDbFixture` en su método `globalFixtures()`.
Esto significa que sólo necesitas trabajar con `@app/tests/fixtures/initdb.php` si quieres realizar algún trabajo de inicialización
antes de cada test. Sino puedes simplemente enfocarte en desarrollar cada caso de test individual y sus fixtures correspondientes.


Organizar Clases de Fixtures y Archivos de Datos
------------------------------------------------

Por defecto, las clases de fixtures busca los archivos de datos correspondientes dentro de la carpeta `data`, que es una subcarpeta
de la carpeta conteniendo los archivos de clases de fixtures. Puedes seguir esta convención al trabajar en proyectos simples.
Para proyectos más grandes, es probable que a menudo necesites intercambiar entre diferentes archivos de datos para la misma clase de fixture
en diferentes tests. Recomendamos que organices los archivos de datos en forma jerárquica similar
a tus espacios de nombre de clases. Por ejemplo,

```
# bajo la carpeta 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
# y así sucesivamente
```

De esta manera evitarás la colisión de archivos de datos de fixtures entre tests y podrás utlilizarlos como necesites.

> Note: En el ejemplo de arriba los archivos de fixtures son nombrados así sólo como ejemplo. En la vida real deberías nombrarlos
> de acuerdo a qué clase de fixture extienden tus clases de fixtures. Por ejemplo, si estás extendiendo
> de [[yii\test\ActiveFixture]] para fixtures de BD, deberías utilizar nombres de tabla de la BD como nombres de los archivos de fixtures;
> Si estás extendiendo de [[yii\mongodb\ActiveFixture]] para fixtures de MongoDB, deberías utilizar nombres de colecciones para los nombres de archivo.

Se puede utilizar una jerarquía similar para organizar archivos de clases de fixtures. En vez de utilizar `data` como directorio raíz, podrías
querer utilizar `fixtures` como directorio raíz para evitar conflictos con los archivos de datos.


Resumen
-------

> Note: Esta sección se encuentra en desarrollo.

Arriba, definimos cómo definir y utilizar fixtures. Abajo resumiremos el típico flujo de trabajo
de correr tests de unidad relacionados a BD:

1. Usa la herramienta `yii migrate` para actualizar tu base de datos de prueba a la última versión;
2. Corre el caso de test:
   - Carga los fixtures: limpia las tablas de la BD relevantes y cargala con los datos de los fixtures;
   - Realiza el test en sí;
   - Descarga los fixtures.
3. Repite el Paso 2 hasta que todos los tests terminen.


**Lo siguiente, a ser limpiado**

Administrar Fixtures
====================

> Note: Esta sección está en desarrollo.
>
> todo: este tutorial podría ser unificado con la parte de arriba en test-fixtures.md

Los fixtures son una parte importante del testing. Su principal propósito es el de poblarte con datos necesarios para el test
de diferentes casos. Con estos datos. utilizar tests se vuelve más eficiente y útil.

Yii soporta fixtures a través de la herramienta de línea de comandos `yii fixture`. Esta herramienta soporta:

* Cargar fixtures a diferentes almacenamientos: RDBMS, NoSQL, etc;
* Descargar fixtures de diferentes maneras (usualmente limpiando el almacenamiento);
* Auto-generar fixtures y poblarlos con datos al azar.

Formato de Fixtures
-------------------

Los fixtures son objetos con diferentes métodos y configuraciones, inspecciónalos en la [documentación oficial](https://github.com/yiisoft/yii2/blob/master/docs/guide-es/test-fixtures.md).
Asumamos que tenemos datos de fixtures a cargar:

```
#archivo users.php bajo la ruta de los fixtures, por defecto @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',
    ],
];
```
Si estamos utilizando un fixture que carga datos en la base de datos, entonces esos registros serán insertados en la tabla `users`. Si estamos utilizando fixtures no sql, por ejemplo de `mongodb`,
entonces estos datos serán aplicados a la colección mongodb `users`. Para aprender cómo implementar varias estrategias de carga y más, visita la [documentación oficial](https://github.com/yiisoft/yii2/blob/master/docs/guide-es/test-fixtures.md).
El fixture de ejemplo de arriba fue autogenerado por la extensión `yii2-faker`, lee más acerca de esto en su [sección](#auto-generating-fixtures).
Los nombres de clase de fixtures no deberían ser en plural.

Cargar fixtures
----------------

Las clases de fixture deberían tener el prefijo `Fixture`. Por defecto los fixtures serán buscados bajo el espacio de nombre `tests\unit\fixtures`, puedes
modificar este comportamiento con opciones de comando o configuración. Puedes excluir algunos fixtures para carga o descarga especificando `-` antes de su nombre, por ejemplo `-User`.

Para cargar un fixture, ejecuta el siguiente comando:

```
yii fixture/load <fixture_name>
```

El parámetro requerido `fixture_name` especifica un nombre de fixture cuyos datos serán cargados. Puedes cargar varios fixtures de una sola vez.
Abajo se muestran formatos correctos de este comando:

```
// carga el fixture `User`
yii fixture/load User

// lo mismo que arriba, dado que la acción por defecto del comando "fixture" es "load"
yii fixture User

// carga varios fixtures
yii fixture "User, UserProfile"

// carga todos los fixtures
yii fixture/load "*"

// lo mismo que arriba
yii fixture "*"

// carga todos los fixtures excepto uno
yii fixture "*, -DoNotLoadThisOne"

// carga fixtures, pero los busca en diferente espacio de nombre. El espacio de nombre por defecto es: tests\unit\fixtures.
yii fixture User --namespace='alias\my\custom\namespace'

// carga el fixture global `some\name\space\CustomFixture` antes de que otros fixtures sean cargados.
// Por defecto está opción se define como `InitDbFixture` para habilitar/deshabilitar la comprobación de integridad. Puedes especificar varios
// fixtures globales separados por coma.
yii fixture User --globalFixtures='some\name\space\Custom'
```

Descargar fixtures
------------------

Para descargar un fixture, ejecuta el siguiente comando:

```
// descarga el fixture Users, por defecto limpiará el almacenamiento del fixture (por ejemplo la tabla "users", o la colección "users" si es un fixture mongodb).
yii fixture/unload User

// descarga varios fixtures
yii fixture/unload "User, UserProfile"

// descarga todos los fixtures
yii fixture/unload "*"

// descarga todos los fixtures excepto uno
yii fixture/unload "*, -DoNotUnloadThisOne"

```

Opciones de comando similares como: `namespace`, `globalFixtures` también pueden ser aplicadas a este comando.

Configurar el Comando Globalmente
---------------------------------
Mientras que las opciones de línea de comandos nos permiten configurar el comando de migración
en el momento, a veces queremos configurar el comando de una vez y para siempre. Por ejemplo puedes configurar
diferentes rutas de migración como a continuación:

```
'controllerMap' => [
    'fixture' => [
        'class' => 'yii\console\controllers\FixtureController',
        'namespace' => 'myalias\some\custom\namespace',
        'globalFixtures' => [
            'some\name\space\Foo',
            'other\name\space\Bar'
        ],
    ],
]
```

Autogenerando fixtures
----------------------

Yii puede también autogenerar fixtures por tí basándose en algún template. Puedes generar tus fixtures con distintos datos en diferentes lenguajes y formatos.
Esta característica es realizada por la librería [Faker](https://github.com/fzaninotto/Faker) y la extensión `yii2-faker`.
Visita la [guía de la extensión](https://github.com/yiisoft/yii2-faker) para mayor documentación.
