Commit 2dec35cf by Qiang Xue

Added fixture classes.

parent eade9dd1
<?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;
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. When loading an ActiveFixture, the corresponding
* database table will be [[resetTable()|reset]] first. It will then be populated with the data loaded by [[loadData()]].
*
* You can access the loaded data via the [[rows]] 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 Fixture
{
/**
* @inheritdoc
*/
public $depends = ['yii\test\DbFixture'];
/**
* @var Connection|string the DB connection object or the application component ID of the DB connection.
* After the ActiveFixture object is created, if you want to change this property, you should only assign it
* with a DB connection object.
*/
public $db = 'db';
/**
* @var string the AR model class associated with this fixture.
* @see tableName
*/
public $modelClass;
/**
* @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 array the data rows. Each array element represents one row of data (column name => column value).
*/
public $rows;
/**
* @var TableSchema the table schema for the table associated with this fixture
*/
private $_table;
/**
* @var \yii\db\ActiveRecord[] the loaded AR models
*/
private $_models;
/**
* @inheritdoc
*/
public function init()
{
parent::init();
if (!isset($this->modelClass) && !isset($this->tableName)) {
throw new InvalidConfigException('Either "modelClass" or "tableName" must be set.');
}
if (is_string($this->db)) {
$this->db = Yii::$app->getComponent($this->db);
}
if (!$this->db instanceof Connection) {
throw new InvalidConfigException("The 'db' property must be either a DB connection instance or the application component ID of a DB connection.");
}
}
/**
* @inheritdoc
*/
public function load()
{
$table = $this->getTableSchema();
$this->resetTable($table);
$this->rows = [];
foreach ($this->loadData() 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->rows[$alias] = $row;
}
}
/**
* 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->rows[$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->rows[$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);
}
/**
* @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 fixture data.
*
* The default implementation will look for a file under the `data` sub-directory of the directory containing
* the fixture class. The file name is assumed to be the same as the table name (with schema prefix if necessary).
* For example, the `tbl_user` table corresponds to the `data/tbl_user.php` file; the `test.tbl_post` table (`test`
* is the non-default schema name) corresponds to `data/test.tbl_user.php`. 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.
*
* You may override this method if you want to change the default way of data loading.
*
* @return array the data rows to be inserted into the database table.
*/
protected function loadData()
{
$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.
* @param TableSchema $table the table to be reset.
*/
protected function resetTable($table)
{
$this->db->createCommand()->delete($table->fullName)->execute();
if ($table->sequenceName !== null) {
$this->db->createCommand()->resetSequence($table->fullName, 1)->execute();
}
}
}
<?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 represents the fixture needed for setting up a DB connection.
*
* 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.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
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';
/**
* @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/init.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 init()
{
parent::init();
if (is_string($this->db)) {
$this->db = Yii::$app->getComponent($this->db);
}
if (!$this->db instanceof Connection) {
throw new InvalidConfigException("The 'db' property must be either a DB connection instance or the application component ID of a DB connection.");
}
}
/**
* @inheritdoc
*/
public function beforeLoad()
{
$this->checkIntegrity(false);
}
/**
* @inheritdoc
*/
public function afterLoad()
{
$this->checkIntegrity(true);
}
/**
* @inheritdoc
*/
public function load()
{
$file = Yii::getAlias($this->initScript);
if (is_file($file)) {
require($file);
}
}
/**
* Enables or disables database integrity check.
* This method may be used to temporarily turn off foreign constraints check.
* @param boolean $check whether to enable database integrity check
*/
public function checkIntegrity($check)
{
foreach ($this->schemas as $schema) {
$this->db->createCommand()->checkIntegrity($check, $schema)->execute();
}
}
}
<?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()
{
}
}
class TestCase extends \Codeception\TestCase\Test
{
public $fixtures;
protected function setUp()
{
parent::setUp();
$this->mockApplication();
$this->loadFixtures();
}
protected function tearDown()
{
$this->unloadFixtures();
$this->destroyApplication();
parent::tearDown();
}
protected function getFixture($name)
{
// return the named fixture object
}
public function fixtures()
{
return [
// anonymous fixture
PostFixture::className(),
// "users" fixture
'users' => UserFixture::className(),
// "cache" fixture with configuration
'cache' => [
'class' => CacheFixture::className(),
'host' => 'xxx',
],
];
}
public function testSave()
{
$user = $this->getFixture('users')->getModel('user1');
$rows = $this->getFixture('users')->getRows();
// or support magic property like the following:
$user = $this->users->getModel('user1');
}
}
class UserFixture extends DbFixture
{
public $modelClass = 'app\models\User';
protected function loadData($table)
{
return require(__DIR__ . '/' . $table->name . '.php');
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment