Fixtures initializer #1956

Closed
Ragazzo opened this Issue Jan 13, 2014 · 112 comments

Comments

Projects
None yet
5 participants
@Ragazzo
Contributor

Ragazzo commented Jan 13, 2014

Currently Yii2 support only one init plain file that will be included before fixtures load. This is good but not enough. For some different cases we may need special initializers per each fixture that will take care of creating triggers, views, maybe adding/deleting fk and other, so to avoid copy-paste between setUp methods we need some class-initializer. For example:


use yii\test\fixture\Initializer;

class UsersInitializer extends Initializer
{
     public function run()
     {
          $this->createTable(); 
          $this->addForeignKey();
          .....
     }
}

After initializer call fixture file will be loaded if it is exists. So ower goals after all:

  1. ability to reuse Initializer in multiple setUp() methods of tests; Avoid copy-paste of init. code in special testing methods (env. creation like triggers, tables, fk and constraints should not be in setUp of test-case, and we should avoid this copy-paste between different test-cases).
  2. add some additional abilitites that will be needed in most cases (user can use initializer to create needed env. fo fixture: tables, fk, triggers, other constraints). Provide migration-like API for working with DB schema;
  3. speed-up database tests, by creating only needed things and not all schema with all tables, etc;
@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 13, 2014

Contributor

Will work on this one and make PR :)

Contributor

Ragazzo commented Jan 13, 2014

Will work on this one and make PR :)

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 13, 2014

Contributor

And as a reminder for myself: add two events before/after fixtures load.
p.s. add ability to use initializers via db-fixture-manager (like it is done for models/rows) with method getInitializer($fixtureName). Because it could be that user may need there some code that should be executed in tearDown() like the opposite of the run() method actions.

Contributor

Ragazzo commented Jan 13, 2014

And as a reminder for myself: add two events before/after fixtures load.
p.s. add ability to use initializers via db-fixture-manager (like it is done for models/rows) with method getInitializer($fixtureName). Because it could be that user may need there some code that should be executed in tearDown() like the opposite of the run() method actions.

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 13, 2014

Contributor

well after some thinking, i guess i need core developers opinions here. @cebe @samdark @qiangxue whats your thoughts on this one?
Provided class will be Initializer with two methods setUp/tearDown but seems it like more standalone class and only have some integration with DbFixtureManager::getInitializer method i doubt should we add this class or not? Yes all benefits that i wrote above are counted, but is it really worth? developer can create such file buy himself, should we provide this base class to add ability to extend it, or not?

Contributor

Ragazzo commented Jan 13, 2014

well after some thinking, i guess i need core developers opinions here. @cebe @samdark @qiangxue whats your thoughts on this one?
Provided class will be Initializer with two methods setUp/tearDown but seems it like more standalone class and only have some integration with DbFixtureManager::getInitializer method i doubt should we add this class or not? Yes all benefits that i wrote above are counted, but is it really worth? developer can create such file buy himself, should we provide this base class to add ability to extend it, or not?

@samdark

This comment has been minimized.

Show comment
Hide comment
@samdark

samdark Jan 14, 2014

Member

I'm for implementing it.

Member

samdark commented Jan 14, 2014

I'm for implementing it.

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 14, 2014

Member

Do we really need this? Note that each fixture is a PHP file. You can put code there to do initialization work. For global initialization work, do it in init.php.

Member

qiangxue commented Jan 14, 2014

Do we really need this? Note that each fixture is a PHP file. You can put code there to do initialization work. For global initialization work, do it in init.php.

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 14, 2014

Contributor

Not that each fixture is a PHP file

currently it is so, for table fixtures, they are simple plain php-files, and end-user may need (i am sure about this) to customize some database env. setup.

For global initialization work, do it in init.php.

yes, but we are not talking about global initialization about partial, see point 3 in the list.

Contributor

Ragazzo commented Jan 14, 2014

Not that each fixture is a PHP file

currently it is so, for table fixtures, they are simple plain php-files, and end-user may need (i am sure about this) to customize some database env. setup.

For global initialization work, do it in init.php.

yes, but we are not talking about global initialization about partial, see point 3 in the list.

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 14, 2014

Member

What is the difference between using plain php-file and Initializer to customize some database env. setup?
Basically my question remains: do we really need to introduce a whole dimension called initializers?

Member

qiangxue commented Jan 14, 2014

What is the difference between using plain php-file and Initializer to customize some database env. setup?
Basically my question remains: do we really need to introduce a whole dimension called initializers?

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 14, 2014

Contributor

What is the difference between using plain php-file and Initializer to customize some database env. setup?

how will you do this with plain php-fixtures files? Example can be like in yii2-advanced, need to tests Users model, it hits database. But you dont need to load all dump, just one small piece. how will you do this? also take into account that fixtures files will be generated automatically by faker so you cant write in them something like Yii::$app->db->createCommand().... And if you will need again this database env. in other test that uses user-fixtures how will you handle it? if copy-paste then see point 1 in list.

Basically my question remains: do we really need to introduce a whole dimension called initializers?

not a big one to be true. There also will be db_cleaner port of ruby gem. Also small but useful.

Contributor

Ragazzo commented Jan 14, 2014

What is the difference between using plain php-file and Initializer to customize some database env. setup?

how will you do this with plain php-fixtures files? Example can be like in yii2-advanced, need to tests Users model, it hits database. But you dont need to load all dump, just one small piece. how will you do this? also take into account that fixtures files will be generated automatically by faker so you cant write in them something like Yii::$app->db->createCommand().... And if you will need again this database env. in other test that uses user-fixtures how will you handle it? if copy-paste then see point 1 in list.

Basically my question remains: do we really need to introduce a whole dimension called initializers?

not a big one to be true. There also will be db_cleaner port of ruby gem. Also small but useful.

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 14, 2014

Member

Alright, I'm convinced. Thanks for explaining.

Now I have some questions about the initializer design.

  1. How many initializers can a fixture have? one or multiple?
  2. How will getInitializer() be implemented?

And also a wild thought: How about we introducing Fixture classes like AssetBundle classes?

  • Fixture classes may have dependencies (like asset bundles).
  • Fixture classes can load and provide fixture data
  • Fixture classes also serve as the initializers as you described above
  • Tests load fixtures by something like: $this->requireFixtures(...fixture class names...)
Member

qiangxue commented Jan 14, 2014

Alright, I'm convinced. Thanks for explaining.

Now I have some questions about the initializer design.

  1. How many initializers can a fixture have? one or multiple?
  2. How will getInitializer() be implemented?

And also a wild thought: How about we introducing Fixture classes like AssetBundle classes?

  • Fixture classes may have dependencies (like asset bundles).
  • Fixture classes can load and provide fixture data
  • Fixture classes also serve as the initializers as you described above
  • Tests load fixtures by something like: $this->requireFixtures(...fixture class names...)
@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 14, 2014

Contributor

How many initializers can a fixture have? one or multiple?

only one.

How will getInitializer() be implemented?

very similar to getModels/getRows. We only need to check if the file with fixtureName+initializerSuffix exists and simply create it. Then run it method start() in DbTestCase in setUp and run method clean in DbTestCase in tearDown. We will know what initializer to create by analyzing property public $fixtures of the DbTestCase. I guess when i submit PR it will be easy to review/understand/suggest.

And also a wild thought: How about we introducing Fixture classes like AssetBundle classes?

i think thats a great idea, some workaround and features can be added in this area.

Fixture classes may have dependencies (like asset bundles).

yes, cool, but we already have public $fixtures also need to note that having dependecies between fixtures should be handled carefully. There should not be only 10 fixtures for all tests in project. Each more or less independent feature should use its own fixtures if it is possible, for example: modules or some components.

Fixture classes can load and provide fixture data

need more talks and suggestions here. I think after basic implementation of initializer we will return to this.

Fixture classes also serve as the initializers as you described above

yep, after implementing initializers we will have more or less understanding where to move on.

Tests load fixtures by something like: $this->requireFixtures(...fixture class names...)

well, we have public $fixtures in DbTestCase + DbFixtureManager. But ofcourse can be discussed after some basic implementation.

Also keep in mind that we have this ticket - #1521 that also will give us good abilities for database clean-up and clean-up strategies. You can see same-named ruby gem for more info.

Contributor

Ragazzo commented Jan 14, 2014

How many initializers can a fixture have? one or multiple?

only one.

How will getInitializer() be implemented?

very similar to getModels/getRows. We only need to check if the file with fixtureName+initializerSuffix exists and simply create it. Then run it method start() in DbTestCase in setUp and run method clean in DbTestCase in tearDown. We will know what initializer to create by analyzing property public $fixtures of the DbTestCase. I guess when i submit PR it will be easy to review/understand/suggest.

And also a wild thought: How about we introducing Fixture classes like AssetBundle classes?

i think thats a great idea, some workaround and features can be added in this area.

Fixture classes may have dependencies (like asset bundles).

yes, cool, but we already have public $fixtures also need to note that having dependecies between fixtures should be handled carefully. There should not be only 10 fixtures for all tests in project. Each more or less independent feature should use its own fixtures if it is possible, for example: modules or some components.

Fixture classes can load and provide fixture data

need more talks and suggestions here. I think after basic implementation of initializer we will return to this.

Fixture classes also serve as the initializers as you described above

yep, after implementing initializers we will have more or less understanding where to move on.

Tests load fixtures by something like: $this->requireFixtures(...fixture class names...)

well, we have public $fixtures in DbTestCase + DbFixtureManager. But ofcourse can be discussed after some basic implementation.

Also keep in mind that we have this ticket - #1521 that also will give us good abilities for database clean-up and clean-up strategies. You can see same-named ruby gem for more info.

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 14, 2014

Member

we have public $fixtures in DbTestCase + DbFixtureManager

where is it?

My idea was actually redesigning the whole fixture stuff.

  • The fixture classes will be the central pieces which are managed by FixtureManager (in analogy to asset bundles managed by AssetManager).
  • A fixture is referenced via its class name, thanks to class autoloading mechanism which makes it very flexible regarding to how to organize fixtures. Using a fixture is like registering an asset bundle.
  • The concept of fixture dependency will allow designing some "macro" fixtures for testing some big features. For example, to test the admin feature, you may design an AdminFixture which depends on UserFixture, PostFixture, etc.
  • There will be no initializers, since fixtures already cover them.
  • Fixtures can take external data files, like the current fixture files, which can be generated by tools like faker.
  • The concept of fixture can be extended, not limiting to DB fixtures.
Member

qiangxue commented Jan 14, 2014

we have public $fixtures in DbTestCase + DbFixtureManager

where is it?

My idea was actually redesigning the whole fixture stuff.

  • The fixture classes will be the central pieces which are managed by FixtureManager (in analogy to asset bundles managed by AssetManager).
  • A fixture is referenced via its class name, thanks to class autoloading mechanism which makes it very flexible regarding to how to organize fixtures. Using a fixture is like registering an asset bundle.
  • The concept of fixture dependency will allow designing some "macro" fixtures for testing some big features. For example, to test the admin feature, you may design an AdminFixture which depends on UserFixture, PostFixture, etc.
  • There will be no initializers, since fixtures already cover them.
  • Fixtures can take external data files, like the current fixture files, which can be generated by tools like faker.
  • The concept of fixture can be extended, not limiting to DB fixtures.
@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 14, 2014

Contributor

we have public $fixtures in DbTestCase + DbFixtureManager

currently in my mind)) because code is not written yet.

well i like all your suggestions but i dont think that currently we will have time on them. for now we need to introduce to the developers some useful tools. After we have something working we can make some redesigning and other to see if it will work or not, but we already will have some fallback implemented.

Contributor

Ragazzo commented Jan 14, 2014

we have public $fixtures in DbTestCase + DbFixtureManager

currently in my mind)) because code is not written yet.

well i like all your suggestions but i dont think that currently we will have time on them. for now we need to introduce to the developers some useful tools. After we have something working we can make some redesigning and other to see if it will work or not, but we already will have some fallback implemented.

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 14, 2014

Contributor

And we also should not end up with something big and over-abstracted-layered like DbUnit or other things.

Contributor

Ragazzo commented Jan 14, 2014

And we also should not end up with something big and over-abstracted-layered like DbUnit or other things.

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 15, 2014

Contributor

@qiangxue also would be interesting to know your opinion on https://github.com/cakephp/cakephp/tree/master/lib/Cake/Test/Fixture as i see you are thinking of something looks a like.

Contributor

Ragazzo commented Jan 15, 2014

@qiangxue also would be interesting to know your opinion on https://github.com/cakephp/cakephp/tree/master/lib/Cake/Test/Fixture as i see you are thinking of something looks a like.

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 15, 2014

Member

Thanks for the pointer. Will look into it.
FYI, I'm already starting working on this. I think we don't need to waste time in developing a temporary solution. We still have time and this new design isn't very complicated.

Member

qiangxue commented Jan 15, 2014

Thanks for the pointer. Will look into it.
FYI, I'm already starting working on this. I think we don't need to waste time in developing a temporary solution. We still have time and this new design isn't very complicated.

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 15, 2014

Contributor

hm, good to know that you are working on it, but as an end-user i would like to point that it should be very easy to setup and use. Because end-user dont want to manage all this deps and configs he only want it work asap, it was one of the main reasons why i like Yii1 fixtures - very simple to write, very simple to use and understand.

P.S. can you also assign this issue to yourself then?

Contributor

Ragazzo commented Jan 15, 2014

hm, good to know that you are working on it, but as an end-user i would like to point that it should be very easy to setup and use. Because end-user dont want to manage all this deps and configs he only want it work asap, it was one of the main reasons why i like Yii1 fixtures - very simple to write, very simple to use and understand.

P.S. can you also assign this issue to yourself then?

@ghost ghost assigned qiangxue Jan 15, 2014

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 15, 2014

Member

Sure. I also want to keep simplicity. Below is a use example (not finalized yet). Your suggestions are welcome.

class UserFixture extends DbFixture
{
    public $modelClass = 'app\models\User';

    public function loadData()
    {
        return [...]; // or require external data file
    }
}


class MyTestCase
{
    protected function setUp()
    {
        $this->getFixtureManager()->load([
            UserFixture::className(),
        ]);
    }

    protected function tearDown()
    {
        $this->getFixtureManager()->unload();
    }

    public function testSave()
    {
        $user1 = UserFixture::getModel('user1');
        $rows = UserFixture::getRows();
    }
}
Member

qiangxue commented Jan 15, 2014

Sure. I also want to keep simplicity. Below is a use example (not finalized yet). Your suggestions are welcome.

class UserFixture extends DbFixture
{
    public $modelClass = 'app\models\User';

    public function loadData()
    {
        return [...]; // or require external data file
    }
}


class MyTestCase
{
    protected function setUp()
    {
        $this->getFixtureManager()->load([
            UserFixture::className(),
        ]);
    }

    protected function tearDown()
    {
        $this->getFixtureManager()->unload();
    }

    public function testSave()
    {
        $user1 = UserFixture::getModel('user1');
        $rows = UserFixture::getRows();
    }
}
@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 15, 2014

Contributor

hm, i like it, but i think that in fixture class loadData sounds not so good. i also would like to have public $fixtures in the MyTestCase extends DbTestCase, rather then each time writing load-unload.

Also is there some code about how it will load plain-php files? I think that after you will end up your work we can add/adjust fixture/generate command to generate also this classes, because they will be very similar. Maybe adjust not yii2-faker (improving faker command) and just adding this command to yii\console\FixtureController.

And i think that Fixture should implemnt __get() and __set() as in Yii1 DbTestCase where () was for model and [] was for row. or maybe make some shor-cut to not use always $neededRow = UserFixture::getRows('myrow');

Contributor

Ragazzo commented Jan 15, 2014

hm, i like it, but i think that in fixture class loadData sounds not so good. i also would like to have public $fixtures in the MyTestCase extends DbTestCase, rather then each time writing load-unload.

Also is there some code about how it will load plain-php files? I think that after you will end up your work we can add/adjust fixture/generate command to generate also this classes, because they will be very similar. Maybe adjust not yii2-faker (improving faker command) and just adding this command to yii\console\FixtureController.

And i think that Fixture should implemnt __get() and __set() as in Yii1 DbTestCase where () was for model and [] was for row. or maybe make some shor-cut to not use always $neededRow = UserFixture::getRows('myrow');

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 15, 2014

Member

Yes, adding $fixtures is fine. It would require us to provide DbTestCase base class though.
Right now I'm struggling to determine how to pass some critical information to the fixture objects (such as db connection). Note that here we are talking about traditional databases, but we should also consider nosql databases and other test fixtures as well.

Member

qiangxue commented Jan 15, 2014

Yes, adding $fixtures is fine. It would require us to provide DbTestCase base class though.
Right now I'm struggling to determine how to pass some critical information to the fixture objects (such as db connection). Note that here we are talking about traditional databases, but we should also consider nosql databases and other test fixtures as well.

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 15, 2014

Contributor

(such as db connection)

i guess they can be taken in DbFixtureManager::load(), just pass them to fixture new $fixtureClass(['db' => $this->db]). Like in createCommand for db-connection.

Note that here we are talking about traditional databases, but we should also consider nosql databases and other test fixtures as well.

true, thats why i was considering to invite here @cebe and @klimov-paul , but looks like they are busy enough now. I will also need their help in this issue.

Contributor

Ragazzo commented Jan 15, 2014

(such as db connection)

i guess they can be taken in DbFixtureManager::load(), just pass them to fixture new $fixtureClass(['db' => $this->db]). Like in createCommand for db-connection.

Note that here we are talking about traditional databases, but we should also consider nosql databases and other test fixtures as well.

true, thats why i was considering to invite here @cebe and @klimov-paul , but looks like they are busy enough now. I will also need their help in this issue.

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 15, 2014

Member

If an application needs two different DBs (one mysql, the other sqlite or redis), what kind of fixture manager should we use? one manager or two?

Member

qiangxue commented Jan 15, 2014

If an application needs two different DBs (one mysql, the other sqlite or redis), what kind of fixture manager should we use? one manager or two?

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 15, 2014

Contributor

i guess that db could be an array also. Or if needed user can explicitly set db property of the DbFixtureManager in setUp and then bring it back in tearDown, or third approach:

public $fixtures = [
   'tbl_user' => [
       'db' => 'some_my_connection'
   ],
];

since fixtures are objects now. But need to deal with aliasing i guess, like in current DbFixtureManager::load() when $fixtures can be in different ways: simple array, array of arrays, etc (https://github.com/yiisoft/yii2/blob/master/framework/test/DbTestTrait.php#L69).

Contributor

Ragazzo commented Jan 15, 2014

i guess that db could be an array also. Or if needed user can explicitly set db property of the DbFixtureManager in setUp and then bring it back in tearDown, or third approach:

public $fixtures = [
   'tbl_user' => [
       'db' => 'some_my_connection'
   ],
];

since fixtures are objects now. But need to deal with aliasing i guess, like in current DbFixtureManager::load() when $fixtures can be in different ways: simple array, array of arrays, etc (https://github.com/yiisoft/yii2/blob/master/framework/test/DbTestTrait.php#L69).

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 15, 2014

Member

Well, my plan is that $fixtures only takes configuration of fixture classes. All fixture data must be loaded through the corresponding fixture classes.

Member

qiangxue commented Jan 15, 2014

Well, my plan is that $fixtures only takes configuration of fixture classes. All fixture data must be loaded through the corresponding fixture classes.

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 15, 2014

Contributor

Well, my plan is that $fixtures only takes configuration of fixture classes

hm, but the third approach is just about, no?

Contributor

Ragazzo commented Jan 15, 2014

Well, my plan is that $fixtures only takes configuration of fixture classes

hm, but the third approach is just about, no?

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 15, 2014

Member

What's the third approach?

Member

qiangxue commented Jan 15, 2014

What's the third approach?

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 15, 2014

Contributor
public $fixtures = [
   'tbl_user' => [
       'db' => 'some_my_connection'
   ],
];

Well, my plan is that $fixtures only takes configuration of fixture classes.

or i misunderstand something?)

Contributor

Ragazzo commented Jan 15, 2014

public $fixtures = [
   'tbl_user' => [
       'db' => 'some_my_connection'
   ],
];

Well, my plan is that $fixtures only takes configuration of fixture classes.

or i misunderstand something?)

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 15, 2014

Member

tbl_user should be app\test\fixtures\UserFixture.
Yes, configuration like this is supported.

Member

qiangxue commented Jan 15, 2014

tbl_user should be app\test\fixtures\UserFixture.
Yes, configuration like this is supported.

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 15, 2014

Contributor

so, whats the problem then?) if we can explicitly set db to the fixture if needed, like:

public $fixtures = [
   'tbl_user' => [
       'db' => 'mongo_node_2'
   ],
];

tbl_user should be app\test\fixtures\UserFixture

well, i think that if class is not set then it can be done by simple usage of StringHelper methods to get needed name for class-name.

Contributor

Ragazzo commented Jan 15, 2014

so, whats the problem then?) if we can explicitly set db to the fixture if needed, like:

public $fixtures = [
   'tbl_user' => [
       'db' => 'mongo_node_2'
   ],
];

tbl_user should be app\test\fixtures\UserFixture

well, i think that if class is not set then it can be done by simple usage of StringHelper methods to get needed name for class-name.

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 15, 2014

Member

What does fixture manager do? What about disabling/enabling integrity checks? what about loading global init scripts?

Member

qiangxue commented Jan 15, 2014

What does fixture manager do? What about disabling/enabling integrity checks? what about loading global init scripts?

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 15, 2014

Contributor

What about disabling/enabling integrity checks? what about loading global init scripts?

yes, that what he should do i guess, disable integrity checks before loading all data. Same work, but just with correction that fixtures are now objects.

Contributor

Ragazzo commented Jan 15, 2014

What about disabling/enabling integrity checks? what about loading global init scripts?

yes, that what he should do i guess, disable integrity checks before loading all data. Same work, but just with correction that fixtures are now objects.

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 15, 2014

Member

What if we need multiple DBs? How many fixture managers do we need? Or do we need fixture manager at all?
The situation here seems to be similar to ActiveRecord which doesn't need things like ActiveRecordManager.

Member

qiangxue commented Jan 15, 2014

What if we need multiple DBs? How many fixture managers do we need? Or do we need fixture manager at all?
The situation here seems to be similar to ActiveRecord which doesn't need things like ActiveRecordManager.

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 15, 2014

Contributor

What if we need multiple DBs? How many fixture managers do we need? Or do we need fixture manager at all?

but we can define db connection in DbTestCase in $fixtures.

Or do we need fixture manager at all?

i guess yes, it will call all fixtures methods and disable/enable integrity checks + init. script.

Contributor

Ragazzo commented Jan 15, 2014

What if we need multiple DBs? How many fixture managers do we need? Or do we need fixture manager at all?

but we can define db connection in DbTestCase in $fixtures.

Or do we need fixture manager at all?

i guess yes, it will call all fixtures methods and disable/enable integrity checks + init. script.

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 15, 2014

Member

Disabling/enabling integrity checks requires DB connection. How to configure that? And if we have different kinds of DBs (mysql and redis, for example), should we have a single fixture manager or multiple?

Member

qiangxue commented Jan 15, 2014

Disabling/enabling integrity checks requires DB connection. How to configure that? And if we have different kinds of DBs (mysql and redis, for example), should we have a single fixture manager or multiple?

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 15, 2014

Contributor

Disabling/enabling integrity checks requires DB connection

afaik, all redis/mongo/elastic have Connection class.

And if we have different kinds of DBs (mysql and redis, for example), should we have a single fixture manager or multiple?

hm, i guess one, but for disabling/enabling integrity checks it should first group fixtures according to what category they belongs and then disable/enable integrity for current groups, example

$differentFixtures = ...

foreach ($differentFixtures as $n=>$f) {
   // $this->_fixtures[:group] = $this->determineFixtureConnection($f);
}

....
foreach($this->fixtures as $group=>$fixtures) {
//disable integrity
//load fixtures
//enable integrity
}

or we may just put enable/disable integrity checks in Fixture class, and just leave fixture-manager as requiring init.php file.

Contributor

Ragazzo commented Jan 15, 2014

Disabling/enabling integrity checks requires DB connection

afaik, all redis/mongo/elastic have Connection class.

And if we have different kinds of DBs (mysql and redis, for example), should we have a single fixture manager or multiple?

hm, i guess one, but for disabling/enabling integrity checks it should first group fixtures according to what category they belongs and then disable/enable integrity for current groups, example

$differentFixtures = ...

foreach ($differentFixtures as $n=>$f) {
   // $this->_fixtures[:group] = $this->determineFixtureConnection($f);
}

....
foreach($this->fixtures as $group=>$fixtures) {
//disable integrity
//load fixtures
//enable integrity
}

or we may just put enable/disable integrity checks in Fixture class, and just leave fixture-manager as requiring init.php file.

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 16, 2014

Contributor

@qiangxue i also think that Fixture classes should have following two properties:

  1. createSchema - if need to create schema-data (tables, views, triggers,etc)
  2. populateData - if need to populate tables with data.

Why is this needed? Well because of situations when developer dont want to populate data and just want to make some test with tables without data.

Why having createSchema - same case, user may have his own unique created already db-schema, and just want to load data without re-creating. This also will help us to manage changes in FixtureController because its more about loading data to already created tables rather then recreating tables from bottom. So the only dirty hack in FixtureController::apply will be like

//set here for each fixture property `createSchema` = false;
...
$this->dbFixtureManager->load($fixtures);

So overall Fixture class method setUp (i think it should be named so by other tools special methods conventions) will be like:

public function setUp()
{
     //disable integrity check
     if ($this->createSchema) {
          $this->createSchema();
     }
     if ($this->populateData) {
          $this->populateData();
     }
    //enable integrity check
}

and yes, fixtures dependencies will be good feature :) but we should provide good docs and examples how to use it to avoid overusing and creating complex dependencies.

Contributor

Ragazzo commented Jan 16, 2014

@qiangxue i also think that Fixture classes should have following two properties:

  1. createSchema - if need to create schema-data (tables, views, triggers,etc)
  2. populateData - if need to populate tables with data.

Why is this needed? Well because of situations when developer dont want to populate data and just want to make some test with tables without data.

Why having createSchema - same case, user may have his own unique created already db-schema, and just want to load data without re-creating. This also will help us to manage changes in FixtureController because its more about loading data to already created tables rather then recreating tables from bottom. So the only dirty hack in FixtureController::apply will be like

//set here for each fixture property `createSchema` = false;
...
$this->dbFixtureManager->load($fixtures);

So overall Fixture class method setUp (i think it should be named so by other tools special methods conventions) will be like:

public function setUp()
{
     //disable integrity check
     if ($this->createSchema) {
          $this->createSchema();
     }
     if ($this->populateData) {
          $this->populateData();
     }
    //enable integrity check
}

and yes, fixtures dependencies will be good feature :) but we should provide good docs and examples how to use it to avoid overusing and creating complex dependencies.

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 16, 2014

Member

A rough design at https://gist.github.com/qiangxue/89ab1d196570dcebd1ef

Explanations:

  • Each fixture is an object of Fixture child class.
  • There is no fixture manager anymore. TestCase is responsible for loading and unloading fixtures (the actual work is done within each fixture. TestCase mainly take care of the dependency handling).
  • A fixture can have dependencies. A dependent fixture will be loaded BEFORE a depending fixture, and unloaded AFTER a depending fixture.
  • There is no more init script. It is replaced by a global fixture class which is depended by other fixtures. How to specify this more easily?
  • DB Integrity check is toggled in the global fixture class.

Define a DB fixture

The actual API is TBD.

class UserFixture extends DbFixture
{
    public $modelClass = 'app\models\User';

    protected function loadData($table)
    {
        return [...data...];
        // or return from external data file
        return require(__DIR__ . '/' . $table->name . '.php');
    }
}

Using fixtures

class MyTestCase extends TestCase
{
    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');
    }
}
Member

qiangxue commented Jan 16, 2014

A rough design at https://gist.github.com/qiangxue/89ab1d196570dcebd1ef

Explanations:

  • Each fixture is an object of Fixture child class.
  • There is no fixture manager anymore. TestCase is responsible for loading and unloading fixtures (the actual work is done within each fixture. TestCase mainly take care of the dependency handling).
  • A fixture can have dependencies. A dependent fixture will be loaded BEFORE a depending fixture, and unloaded AFTER a depending fixture.
  • There is no more init script. It is replaced by a global fixture class which is depended by other fixtures. How to specify this more easily?
  • DB Integrity check is toggled in the global fixture class.

Define a DB fixture

The actual API is TBD.

class UserFixture extends DbFixture
{
    public $modelClass = 'app\models\User';

    protected function loadData($table)
    {
        return [...data...];
        // or return from external data file
        return require(__DIR__ . '/' . $table->name . '.php');
    }
}

Using fixtures

class MyTestCase extends TestCase
{
    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');
    }
}
@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 16, 2014

Contributor

A fixture can have dependencies. A dependent fixture will be loaded BEFORE a depending fixture, and unloaded AFTER a depending fixture.

hm, but that a very critical question also, because of some dependent fixtures could be loaded also after the fixture, example: UsersFixture loaded first, and then UsersProfilesFixture. So i think that depends property should be split into two: before and after - according to load (reverse it would be for unload). If there is no before or after then fixtures are counting as before (same as you suggested above).

There is no more init script. It is replaced by a global fixture class which is depended by other fixtures. How to specify this more easily?

hm, not sure but i guess that property initScript or something like this will work fine. It will be in base fixture class and will work like template-method for other fixtures, because of they are extended. Smth like this:

class Fixture extends Component
{
    public $initScript;

    public function setUp()
    {
        require(Yii::getAlias($this->initScript));
    }
}

class UsersFixture extends Fixture
{
    public function setUp()
    {
        parent::setUp();
        //do your code here
    }
}

DB Integrity check is toggled in the global fixture class.

k, but would be great to see it in PR later)

Define a DB fixture

currently dont like methods:

       $user = $this->getFixture('users')->getModel('user1');
        $rows = $this->getFixture('users')->getRows();
        // or support magic property like the following:
        $user = $this->users->getModel('user1');

can we add method same-named as fixtures on the fly to the test-case class?

$user = $this->user('user1'); //returns AR model
$user = $this->user['user1']; //returns fixture array element

that would be more readable and easy for end user as i think.

Contributor

Ragazzo commented Jan 16, 2014

A fixture can have dependencies. A dependent fixture will be loaded BEFORE a depending fixture, and unloaded AFTER a depending fixture.

hm, but that a very critical question also, because of some dependent fixtures could be loaded also after the fixture, example: UsersFixture loaded first, and then UsersProfilesFixture. So i think that depends property should be split into two: before and after - according to load (reverse it would be for unload). If there is no before or after then fixtures are counting as before (same as you suggested above).

There is no more init script. It is replaced by a global fixture class which is depended by other fixtures. How to specify this more easily?

hm, not sure but i guess that property initScript or something like this will work fine. It will be in base fixture class and will work like template-method for other fixtures, because of they are extended. Smth like this:

class Fixture extends Component
{
    public $initScript;

    public function setUp()
    {
        require(Yii::getAlias($this->initScript));
    }
}

class UsersFixture extends Fixture
{
    public function setUp()
    {
        parent::setUp();
        //do your code here
    }
}

DB Integrity check is toggled in the global fixture class.

k, but would be great to see it in PR later)

Define a DB fixture

currently dont like methods:

       $user = $this->getFixture('users')->getModel('user1');
        $rows = $this->getFixture('users')->getRows();
        // or support magic property like the following:
        $user = $this->users->getModel('user1');

can we add method same-named as fixtures on the fly to the test-case class?

$user = $this->user('user1'); //returns AR model
$user = $this->user['user1']; //returns fixture array element

that would be more readable and easy for end user as i think.

@cebe

This comment has been minimized.

Show comment
Hide comment
@cebe

cebe Jan 16, 2014

Member

And also a wild thought: How about we introducing Fixture classes like AssetBundle classes?

I really like this idea. Your design of simple load and unload looks useable for any kind of db which is nice for nosql. Had no time to read the whole discussion here but it looks like it is going into the right direction :-)

Member

cebe commented Jan 16, 2014

And also a wild thought: How about we introducing Fixture classes like AssetBundle classes?

I really like this idea. Your design of simple load and unload looks useable for any kind of db which is nice for nosql. Had no time to read the whole discussion here but it looks like it is going into the right direction :-)

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 18, 2014

Contributor

Also, why should a fixture contain schema change instructions?

m? see p 1. 2. in the issue description.

Anyway FixtureController should not be removed. Because we add some complex code for end-users (fixtures classes, etc). And i think that developer would switch to another fw rather then trying again after first fail with get himself known to new fixtures.

It is always possible that a test may not end cleanly and thus the database may contain some leftover schema changes.

i think we should wrap loadFixtures/unloadFixtures in transaction.

Contributor

Ragazzo commented Jan 18, 2014

Also, why should a fixture contain schema change instructions?

m? see p 1. 2. in the issue description.

Anyway FixtureController should not be removed. Because we add some complex code for end-users (fixtures classes, etc). And i think that developer would switch to another fw rather then trying again after first fail with get himself known to new fixtures.

It is always possible that a test may not end cleanly and thus the database may contain some leftover schema changes.

i think we should wrap loadFixtures/unloadFixtures in transaction.

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 18, 2014

Member

Also, why should a fixture contain schema change instructions?

I'm not very convinced we should do this in fixture classes. The fixture classes are mainly used to represent state that may be changed during tests. Database schemas are normally very static, unless your code to be tested is dealing with database schema changes, but then you probably should separate them into different fixtures. I also don't think it's a good idea to apply the DB migration idea here.

Member

qiangxue commented Jan 18, 2014

Also, why should a fixture contain schema change instructions?

I'm not very convinced we should do this in fixture classes. The fixture classes are mainly used to represent state that may be changed during tests. Database schemas are normally very static, unless your code to be tested is dealing with database schema changes, but then you probably should separate them into different fixtures. I also don't think it's a good idea to apply the DB migration idea here.

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 18, 2014

Member

i think we should wrap loadFixtures/unloadFixtures in transaction.

It should be done in DbFixture because FixtureTrait deals with generic fixture classes. I'm not sure if we should this though.

Member

qiangxue commented Jan 18, 2014

i think we should wrap loadFixtures/unloadFixtures in transaction.

It should be done in DbFixture because FixtureTrait deals with generic fixture classes. I'm not sure if we should this though.

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 18, 2014

Contributor

I'm not very convinced we should do this in fixture classes.

Nope, this all discussion was started about to make it easy for end-developer, reducing copy-paste and increasing db-testing time. If it will not be made, then i dont see real benefits of all this. I dont want to make this huge copy-paste each time in setUp and tearDown and i also dont need to load all 200 tables in testing just 1 AR.

Then there is no need for yii2-test because there is little benefit and has many drawbacks by doing so. Our main criteria for creating an extension is the part of the code has external dependency.

it will have mongo/redis and other fixtures classes, is not it external?

Contributor

Ragazzo commented Jan 18, 2014

I'm not very convinced we should do this in fixture classes.

Nope, this all discussion was started about to make it easy for end-developer, reducing copy-paste and increasing db-testing time. If it will not be made, then i dont see real benefits of all this. I dont want to make this huge copy-paste each time in setUp and tearDown and i also dont need to load all 200 tables in testing just 1 AR.

Then there is no need for yii2-test because there is little benefit and has many drawbacks by doing so. Our main criteria for creating an extension is the part of the code has external dependency.

it will have mongo/redis and other fixtures classes, is not it external?

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 18, 2014

Member

Nope, this all discussion was started about to make it easy for end-developer, reducing copy-paste and increasing db-testing time. If it will not be made, then i dont see real benefits of all this.

I re-read your initial proposal. I'm certain now that the schema change (and other env setup instructions) should be written in different fixture classes, rather than mixing them with data fixtures. This is because as you said, you may already have the environment, and you only want to apply the data, or vice versa. It's more complicated for users to toggle a property of a fixture than asking them to specify which fixtures to apply. And your proposal only covers DB fixtures.

it will have mongo/redis and other fixtures classes, is not it external?

The specialized fixture classes will be included within each extension. It doesn't scale well if we put them all under yii2-test. It also makes yii2-test depends on many things.

Member

qiangxue commented Jan 18, 2014

Nope, this all discussion was started about to make it easy for end-developer, reducing copy-paste and increasing db-testing time. If it will not be made, then i dont see real benefits of all this.

I re-read your initial proposal. I'm certain now that the schema change (and other env setup instructions) should be written in different fixture classes, rather than mixing them with data fixtures. This is because as you said, you may already have the environment, and you only want to apply the data, or vice versa. It's more complicated for users to toggle a property of a fixture than asking them to specify which fixtures to apply. And your proposal only covers DB fixtures.

it will have mongo/redis and other fixtures classes, is not it external?

The specialized fixture classes will be included within each extension. It doesn't scale well if we put them all under yii2-test. It also makes yii2-test depends on many things.

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 18, 2014

Contributor

I re-read your initial proposal. I'm certain now that the schema change (and other env setup instructions) should be written in different fixture classes, rather than mixing them with data fixtures.

So developer need to create 2 Fixtures for just loading 1 dump to table? SchemaUsersFixture, DataUsersFixture? Dont think that this files zoo is good :)

Contributor

Ragazzo commented Jan 18, 2014

I re-read your initial proposal. I'm certain now that the schema change (and other env setup instructions) should be written in different fixture classes, rather than mixing them with data fixtures.

So developer need to create 2 Fixtures for just loading 1 dump to table? SchemaUsersFixture, DataUsersFixture? Dont think that this files zoo is good :)

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 18, 2014

Member

I don't understand your use case. If your database schema is different from the dumped one, you should apply DB migration to synchronize it. If you they are the same, then you should only dump the data rather than both. If you just want to have a quick test of some dumped data, and you don't want to bother DB migration etc., then fixture isn't the right tool and isn't designed for this purpose.

Member

qiangxue commented Jan 18, 2014

I don't understand your use case. If your database schema is different from the dumped one, you should apply DB migration to synchronize it. If you they are the same, then you should only dump the data rather than both. If you just want to have a quick test of some dumped data, and you don't want to bother DB migration etc., then fixture isn't the right tool and isn't designed for this purpose.

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 18, 2014

Contributor

@qiangxue so your suggestions is to make createTable and other things in setUp and tearDown methods of test-cases? and make this copy-paste each time on 30 tests, and then after some small change edit all this tests? I dont think then that this issue makes sense, so it could be just closed, because main feature was p. 1, 2 in issue description.

Contributor

Ragazzo commented Jan 18, 2014

@qiangxue so your suggestions is to make createTable and other things in setUp and tearDown methods of test-cases? and make this copy-paste each time on 30 tests, and then after some small change edit all this tests? I dont think then that this issue makes sense, so it could be just closed, because main feature was p. 1, 2 in issue description.

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 18, 2014

Member

Nope, I don't mean that. I only meant that schema and data fixtures should be separated rather than mixed. Schema fixtures can be loaded only once or not loaded at all, while data fixtures need to be loaded in setUp.

Member

qiangxue commented Jan 18, 2014

Nope, I don't mean that. I only meant that schema and data fixtures should be separated rather than mixed. Schema fixtures can be loaded only once or not loaded at all, while data fixtures need to be loaded in setUp.

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 18, 2014

Contributor

Still dont understand you, you want to remove initSchema or you talking about not implementing flag createSchema ? If first then this issue is senseless :)

Contributor

Ragazzo commented Jan 18, 2014

Still dont understand you, you want to remove initSchema or you talking about not implementing flag createSchema ? If first then this issue is senseless :)

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 18, 2014

Member

Yes, I'm questioning the need of initSchema (and thus createSchema) because I don't understand the use case (see #1956 (comment))

Member

qiangxue commented Jan 18, 2014

Yes, I'm questioning the need of initSchema (and thus createSchema) because I don't understand the use case (see #1956 (comment))

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 18, 2014

Contributor

Nope, initSchema is needed definitly see p1,2 in issue description. It will simplify testing in other way problems of Yii1 will not be solved here and all this fixture-objects dont make sense because they are complex if comparing to fixture-manager and dont introduce anything new.

Contributor

Ragazzo commented Jan 18, 2014

Nope, initSchema is needed definitly see p1,2 in issue description. It will simplify testing in other way problems of Yii1 will not be solved here and all this fixture-objects dont make sense because they are complex if comparing to fixture-manager and dont introduce anything new.

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 18, 2014

Member

p1. This is just a schema fixture. If your test needs it, declare it in fixtures() and it will load. No copy-paste needed.
p2. You use fixture command to apply the schema fixtures.

The new fixture design makes a lot of sense because: it generalizes the fixture concept, not just dealing with DB fixtures; it unifies fixture references (how to refer to a fixture); it allows you to organize fixtures better with the dependency concept. Yii1 FixtureManager doesn't handle any of these.

Is it more complex than Yii1 FixtureManager? Absolute not. For example, you can put the fixture data directly within a fixture class rather than in a different file. The result will be that you have the same number of fixture files as in Yii1 but with greater control and flexibility.

Member

qiangxue commented Jan 18, 2014

p1. This is just a schema fixture. If your test needs it, declare it in fixtures() and it will load. No copy-paste needed.
p2. You use fixture command to apply the schema fixtures.

The new fixture design makes a lot of sense because: it generalizes the fixture concept, not just dealing with DB fixtures; it unifies fixture references (how to refer to a fixture); it allows you to organize fixtures better with the dependency concept. Yii1 FixtureManager doesn't handle any of these.

Is it more complex than Yii1 FixtureManager? Absolute not. For example, you can put the fixture data directly within a fixture class rather than in a different file. The result will be that you have the same number of fixture files as in Yii1 but with greater control and flexibility.

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 18, 2014

Contributor

The new fixture design makes a lot of sense because: it generalizes the fixture concept, not just dealing with DB fixtures; it unifies fixture references (how to refer to a fixture); it allows you to organize fixtures better with the dependency concept. Yii1 FixtureManager doesn't handle any of these.

only talks, no real profit :)

Is it more complex than Yii1 FixtureManager?

yes :) its not complex for you because you developed it.

p1. This is just a schema fixture. If your test needs it, declare it in fixtures() and it will load. No copy-paste needed.

i dont want to create another fixture just for schema. It should be in fixture class of the UserFixture. Thats it.

p2. You use fixture command to apply the schema fixtures.

currently i cant, and i dont want to use it in test, because see above.

@qiangxue i was using fixtures/db-test-case in Yii1 (such a pain) and this features is really needed, i dont get why you still not convinced, and it looks like it will not get anywhere becuase you are core-developer and only see it from your side. Even @samdark said that he is for implementing this, we discussed with him this feature and main thing was about p1,2 and not simple wrapper to load fixtures. So i think a lot of developers (i spoke to some of good russian developers about it) would be dissapointed, and as always Yii will loose opportunity to get good testing support.

Contributor

Ragazzo commented Jan 18, 2014

The new fixture design makes a lot of sense because: it generalizes the fixture concept, not just dealing with DB fixtures; it unifies fixture references (how to refer to a fixture); it allows you to organize fixtures better with the dependency concept. Yii1 FixtureManager doesn't handle any of these.

only talks, no real profit :)

Is it more complex than Yii1 FixtureManager?

yes :) its not complex for you because you developed it.

p1. This is just a schema fixture. If your test needs it, declare it in fixtures() and it will load. No copy-paste needed.

i dont want to create another fixture just for schema. It should be in fixture class of the UserFixture. Thats it.

p2. You use fixture command to apply the schema fixtures.

currently i cant, and i dont want to use it in test, because see above.

@qiangxue i was using fixtures/db-test-case in Yii1 (such a pain) and this features is really needed, i dont get why you still not convinced, and it looks like it will not get anywhere becuase you are core-developer and only see it from your side. Even @samdark said that he is for implementing this, we discussed with him this feature and main thing was about p1,2 and not simple wrapper to load fixtures. So i think a lot of developers (i spoke to some of good russian developers about it) would be dissapointed, and as always Yii will loose opportunity to get good testing support.

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 18, 2014

Member

only talks, no real profit :)

If this is your attitude, I will not argue anymore since I have already given docs and examples.

i dont want to create another fixture just for schema.

You want to put it in an initializer class, but not a fixture class. What an argument?

currently i cant, and i dont want to use it in test, because see above.

True, but that's something to be fixed.

i dont get why you still not convinced,

Because you never answered me. See: #1956 (comment)

Member

qiangxue commented Jan 18, 2014

only talks, no real profit :)

If this is your attitude, I will not argue anymore since I have already given docs and examples.

i dont want to create another fixture just for schema.

You want to put it in an initializer class, but not a fixture class. What an argument?

currently i cant, and i dont want to use it in test, because see above.

True, but that's something to be fixed.

i dont get why you still not convinced,

Because you never answered me. See: #1956 (comment)

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 18, 2014

Contributor

Because you never answered me. See: #1956 (comment)

i answerd you that schema is part of fixture, in this way its more easy to handle testing.

You want to put it in an initializer class, but not a fixture class. What an argument?

argument is p1,2 in issue description.

So overall what is your suggestion? how to handle schema-creating in custom ways? creating +1 schema-fixture for each fixture? bad idea.

Contributor

Ragazzo commented Jan 18, 2014

Because you never answered me. See: #1956 (comment)

i answerd you that schema is part of fixture, in this way its more easy to handle testing.

You want to put it in an initializer class, but not a fixture class. What an argument?

argument is p1,2 in issue description.

So overall what is your suggestion? how to handle schema-creating in custom ways? creating +1 schema-fixture for each fixture? bad idea.

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 18, 2014

Member

Why do you need a schema fixture for each fixture? Why do you want to repeat what you have done in DB migrations? For example, I want to add a column to the user table. Does this mean I need to add a new DB migration as well as modify the initializer with nearly identical code?

Member

qiangxue commented Jan 18, 2014

Why do you need a schema fixture for each fixture? Why do you want to repeat what you have done in DB migrations? For example, I want to add a column to the user table. Does this mean I need to add a new DB migration as well as modify the initializer with nearly identical code?

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 18, 2014

Contributor

Why do you need a schema fixture for each fixture?

because i dont want to make copy-paste of init-schema things (creating tables, fk, etc) each time in different tests where fixture is used.

Why do you want to repeat what you have done in DB migrations?

why are we still talking about migrations? what is the use case of migrations here? no use-case.

For example, I want to add a column to the user table. Does this mean I need to add a new DB migration as well as modify the initializer?

yes, thats why you write your tests not once and forget about it, but also crafting them and working on them during the development time. But once again migrations have nothing to deal with testing (only if we provide MigrateTestCase for testing migrations).

So overall whats your solution, how i should handle custom creating? Example:

class UsersTest extends TestCase
{
    protected function setUp()
    {
            Yii::$app->createCommand()->createTable('users', [...]);
    }
}
class UsersProfilesTest extends TestCase
{
    protected function setUp()
    {
            Yii::$app->createCommand()->createTable('users', [...]);
            Yii::$app->createCommand()->createTable('users_profiles', [...]);
    }
}

So as you see above in example its huge problem of copy-pasting, and it just growing with a big number of tests, so if changed one field in schema, i need to go through all this tests and just fix it rather then simply fixing 1 fixture file. I dont even talk here about some base principles of development as DRY, KISS as you see its all about it.

Contributor

Ragazzo commented Jan 18, 2014

Why do you need a schema fixture for each fixture?

because i dont want to make copy-paste of init-schema things (creating tables, fk, etc) each time in different tests where fixture is used.

Why do you want to repeat what you have done in DB migrations?

why are we still talking about migrations? what is the use case of migrations here? no use-case.

For example, I want to add a column to the user table. Does this mean I need to add a new DB migration as well as modify the initializer?

yes, thats why you write your tests not once and forget about it, but also crafting them and working on them during the development time. But once again migrations have nothing to deal with testing (only if we provide MigrateTestCase for testing migrations).

So overall whats your solution, how i should handle custom creating? Example:

class UsersTest extends TestCase
{
    protected function setUp()
    {
            Yii::$app->createCommand()->createTable('users', [...]);
    }
}
class UsersProfilesTest extends TestCase
{
    protected function setUp()
    {
            Yii::$app->createCommand()->createTable('users', [...]);
            Yii::$app->createCommand()->createTable('users_profiles', [...]);
    }
}

So as you see above in example its huge problem of copy-pasting, and it just growing with a big number of tests, so if changed one field in schema, i need to go through all this tests and just fix it rather then simply fixing 1 fixture file. I dont even talk here about some base principles of development as DRY, KISS as you see its all about it.

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 18, 2014

Member

yes, thats why you write your tests not once and forget about it, but also crafting them and working on them during the development time. But once again migrations have nothing to deal with testing (only if we provide MigrateTestCase for testing migrations).

Migration is used to adjust your DB schemes along the course of your development. Migrations can be applied to both production/development and test databases. Yes, when you make schema changes, you need to adjust fixture data as well - we're not writing test once and forget about it. But it seems to me pointless and error-prone if I have to modify in two places about the schema with the same change. This is even more problematic in a team development environment if because migration will coordinate simultaneous schema changes while in initializers, you have to manually deal with conflicts.

Member

qiangxue commented Jan 18, 2014

yes, thats why you write your tests not once and forget about it, but also crafting them and working on them during the development time. But once again migrations have nothing to deal with testing (only if we provide MigrateTestCase for testing migrations).

Migration is used to adjust your DB schemes along the course of your development. Migrations can be applied to both production/development and test databases. Yes, when you make schema changes, you need to adjust fixture data as well - we're not writing test once and forget about it. But it seems to me pointless and error-prone if I have to modify in two places about the schema with the same change. This is even more problematic in a team development environment if because migration will coordinate simultaneous schema changes while in initializers, you have to manually deal with conflicts.

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 18, 2014

Contributor

@qiangxue lets not talk about migrations to init. db, ok?)) its so wrong that i even cant describe it. and you also was against it in some issue few days ago.

So what is the solution you are suggesting? Migrations does not count.

Contributor

Ragazzo commented Jan 18, 2014

@qiangxue lets not talk about migrations to init. db, ok?)) its so wrong that i even cant describe it. and you also was against it in some issue few days ago.

So what is the solution you are suggesting? Migrations does not count.

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 18, 2014

Member

I think I finally understand what you really want to do - you essentially want to represent a database schema in terms of multiple fixtures (plus an init schema) so that you save time when running tests, because you don't need to re-create the whole database each time. Correct?

Member

qiangxue commented Jan 18, 2014

I think I finally understand what you really want to do - you essentially want to represent a database schema in terms of multiple fixtures (plus an init schema) so that you save time when running tests, because you don't need to re-create the whole database each time. Correct?

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 18, 2014

Contributor

@qiangxue yep, exactly. I was playing with 12 tables (empty tables, just creating structure and dropping them + 2 tables fixtures data) and it took to run 2-3 seconds just 1 test in 1 test-case (simple assertTrue(false)). As you see its a huge time, according to that tests should be run very often.

Contributor

Ragazzo commented Jan 18, 2014

@qiangxue yep, exactly. I was playing with 12 tables (empty tables, just creating structure and dropping them + 2 tables fixtures data) and it took to run 2-3 seconds just 1 test in 1 test-case (simple assertTrue(false)). As you see its a huge time, according to that tests should be run very often.

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 18, 2014

Member

What should be in the init schema? Or how to prepare the initial database?

Member

qiangxue commented Jan 18, 2014

What should be in the init schema? Or how to prepare the initial database?

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 18, 2014

Contributor

What should be in the init schema? Or how to prepare the initial database?

it is the developer choice, we just provide template method for this and thats it. Because he dont need to create all tables just to test 1 AR.

Contributor

Ragazzo commented Jan 18, 2014

What should be in the init schema? Or how to prepare the initial database?

it is the developer choice, we just provide template method for this and thats it. Because he dont need to create all tables just to test 1 AR.

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 18, 2014

Member

I'm no longer questioning the necessity of your proposal. I'm convinced.
I'm here asking for suggestions for the missing part - the initial database preparation.

Member

qiangxue commented Jan 18, 2014

I'm no longer questioning the necessity of your proposal. I'm convinced.
I'm here asking for suggestions for the missing part - the initial database preparation.

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 18, 2014

Contributor

I'm here asking for suggestions for the missing part - the initial database preparation.

you already have it: ActiveFixture::initSchema method for particular fixture and DbFixture::initFile for base initial prepartion.

Contributor

Ragazzo commented Jan 18, 2014

I'm here asking for suggestions for the missing part - the initial database preparation.

you already have it: ActiveFixture::initSchema method for particular fixture and DbFixture::initFile for base initial prepartion.

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 18, 2014

Member

Should we also add a resetTable flag? Or we should only call resetTable() if schema is not created.

Member

qiangxue commented Jan 18, 2014

Should we also add a resetTable flag? Or we should only call resetTable() if schema is not created.

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 18, 2014

Member

Ok, updated code/doc based on the above discussions:

Code: master...feature-fixture
Documentation: https://github.com/yiisoft/yii2/blob/feature-fixture/docs/guide/test-fixture.md

Member

qiangxue commented Jan 18, 2014

Ok, updated code/doc based on the above discussions:

Code: master...feature-fixture
Documentation: https://github.com/yiisoft/yii2/blob/feature-fixture/docs/guide/test-fixture.md

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 19, 2014

Contributor

Should we also add a resetTable flag? Or we should only call resetTable() if schema is not created.

no, it is user choice to decide what he want to reset what he want, in particular we cant guess execatly what is he thinking about in this way. I think resetTable should be removed, because we already have loadSchema.

Also good that dep. of DbFixture by default was removed from ActiveFixture.

So overall it looks fine to me, maybe after some playaround will get some issues/enh. but now its ok.

Also could you then submit fix for FixtureController as we now have loadSchema flag?

Contributor

Ragazzo commented Jan 19, 2014

Should we also add a resetTable flag? Or we should only call resetTable() if schema is not created.

no, it is user choice to decide what he want to reset what he want, in particular we cant guess execatly what is he thinking about in this way. I think resetTable should be removed, because we already have loadSchema.

Also good that dep. of DbFixture by default was removed from ActiveFixture.

So overall it looks fine to me, maybe after some playaround will get some issues/enh. but now its ok.

Also could you then submit fix for FixtureController as we now have loadSchema flag?

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 19, 2014

Contributor

Should this docs be fixed, or i misread something?

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.

Contributor

Ragazzo commented Jan 19, 2014

Should this docs be fixed, or i misread something?

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.

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 19, 2014

Member

I still kept resetTable() but disable it by default for the reason that some people may still prefer the old way that loads test database dump all at once to save the trouble of writing loadSchema().

Perhaps you can help with the change needed for FixtureController? Note that DbFixtureManager will be removed.

Member

qiangxue commented Jan 19, 2014

I still kept resetTable() but disable it by default for the reason that some people may still prefer the old way that loads test database dump all at once to save the trouble of writing loadSchema().

Perhaps you can help with the change needed for FixtureController? Note that DbFixtureManager will be removed.

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 19, 2014

Contributor

I still kept resetTable() but disable it by default for the reason that some people may still prefer the old way that loads test database dump all at once to save the trouble of writing loadSchema().

Ok.

Perhaps you can help with the change needed for FixtureController? Note that DbFixtureManager will be removed.

Right, but i should start work after your PR merged. The only thing will be that i need to search fixture-initializer (fixture-class) by fixture name, and just set loadSchema to false and set fixture-data correct path. So i think your PR can be merged? Great work overall, thank you very much.

Contributor

Ragazzo commented Jan 19, 2014

I still kept resetTable() but disable it by default for the reason that some people may still prefer the old way that loads test database dump all at once to save the trouble of writing loadSchema().

Ok.

Perhaps you can help with the change needed for FixtureController? Note that DbFixtureManager will be removed.

Right, but i should start work after your PR merged. The only thing will be that i need to search fixture-initializer (fixture-class) by fixture name, and just set loadSchema to false and set fixture-data correct path. So i think your PR can be merged? Great work overall, thank you very much.

@qiangxue

This comment has been minimized.

Show comment
Hide comment
@qiangxue

qiangxue Jan 19, 2014

Member

Already merged. Thank you for your critical reviews. It's very helpful.

Member

qiangxue commented Jan 19, 2014

Already merged. Thank you for your critical reviews. It's very helpful.

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 19, 2014

Contributor

Hope, i was not rude)) Will work on FixtureController.

Contributor

Ragazzo commented Jan 19, 2014

Hope, i was not rude)) Will work on FixtureController.

@Ragazzo

This comment has been minimized.

Show comment
Hide comment
@Ragazzo

Ragazzo Jan 19, 2014

Contributor

Closing this one as implemented.

Contributor

Ragazzo commented Jan 19, 2014

Closing this one as implemented.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment