Author: maksim_ka
Date: 2010-05-24 13:16:35 +0200 (Mon, 24 May 2010)
New Revision: 29598
Modified:
plugins/sfPhpunitPlugin/branches/1.2-4/README
Log:
[sfPhpunitPlugin] the doc for next package. finalization.
Modified: plugins/sfPhpunitPlugin/branches/1.2-4/README
===================================================================
--- plugins/sfPhpunitPlugin/branches/1.2-4/README 2010-05-24 06:00:11 UTC
(rev 29597)
+++ plugins/sfPhpunitPlugin/branches/1.2-4/README 2010-05-24 11:16:35 UTC
(rev 29598)
@@ -133,6 +133,8 @@
The same is right for test case classes.
Let's look at the BasePhpunitTestSuite.php in test/phpunit folder. Pay
attenteion to _start and _end methods. You have to use them.
+ <?php
+
class BasePhpunitTestSuite extends sfBasePhpunitTestSuite
implements sfPhpunitContextInitilizerInterface
{
@@ -172,6 +174,8 @@
So all tests that are running will have a context with application 'frontend'
and environment 'test'.
You can change it modifing the file.
+ <?php
+
class BasePhpunitTestSuite extends sfBasePhpunitTestSuite
implements sfPhpunitContextInitilizerInterface
{
@@ -197,33 +201,611 @@
### Fixtures
-#### Basic workflow
+Very important thing in testing process is fixture managing.
+It takes half time from all the test to create good environment.
+So a good mechanism to work with them can save a lot of time.
+Also it helps to keep tests simple and easy to understand.
-#### Fixture aggregators
+There is four places where you can keep your fixtures.
+You can use all of them or just one.
-#### Fixture implementation
+* Own - Fixtures from this directory can be used only be the current
(executing) test.
+* Package - Fixtures from this directory can be used only be tests from the
same directory.
+* Common - Fixtures from this directory can be used from every test case.
+* Symfony - Fixtures from this directory stored in standart symfony folder
(data/fixtures) and can be used from every test case.
+
+First of all you need to choose which fixtures you will use.
+There are two types currently avalable:
+* Doctrine
+* Propel
+
+It's simple example how to define a fixture type you want to use.
+As you can see it is very easy to tell what you want to use.
+You have to implement one of the following interfaces to a testcase class.
+
+ <?php
+
+ class FooTestCase extends PHPUnit_Framework_TestCase
+ implements sfPhpunitFixtureDoctrineAggregator
+ // for propel
+ //implements sfPhpunitFixturePropelAggregator
+ {
+ public function getPackageFixtureDir()
+ {
+ // implement this method
+ }
+
+ public function getOwnFixtureDir()
+ {
+ // implement this method
+ }
+
+ public function getCommonFixtureDir()
+ {
+ // implement this method
+ }
+
+ public function getSymfonyFixtureDir()
+ {
+ // implement this method
+ }
+
+ public function testFoo()
+ {
+ // create fixture object by hands:
+ $fixture = sfPhpunitFixture::build($this, $options = array());
+
+ //now you can use fixture instance to work with fixtures.
+ $path = $fixture->getFileOwn('fixture-file.txt');
+
+ // test code here
+ }
+ }
+
+After you finish with this and run this test case you will see something like
that:
+
+ >> phpunit Created dir /home/maksim/projec...s/FooTestCase
+ >> phpunit Created dir /home/maksim/projec...s/FooTestCase
+ PHPUnit 3.4.12 by Sebastian Bergmann.
+
+ .
+
+ Time: 7 seconds, Memory: 37.00Mb
+
+ OK (1 test, 8 assertions)
+
+It is init task helps you with directories for fixture storing.
+It creates package and own directory if they are not exist.
+
+If you don't want to do even defining methods manualy you can
+extends from sfBasePhpunitTestCase (where all that methods are already
implemented) and implement interface.
+sfBasePhpunitTestCase also contain fixture method which returns instance of
fixture so you need not create it manually.
+
+ <?php
+
+ class FooTestCase extends sfBasePhpunitTestCase
+ implements sfPhpunitFixtureDoctrineAggregator
+ // for propel
+ //implements sfPhpunitFixturePropelAggregator
+ {
+ public function testFoo()
+ {
+ $path = $this->fixture()->getFileOwn('fixture-file.txt');
+
+ // test code here
+ }
+ }
+
+In this case you get standart plugin fixture directories.
+Whole fixture directories are subdirectory of
sf_root_dir/test/phpunit/fixtures dir.
+You can always overwrite any of that methods.
+
+Let's say we have FooTestCase.php file in sf_root_dir/test/phpunit/units
directory.
+Standart directories for the testcase will be:
+
+* Own - sf_root_dir/test/phpunit/fixtures/units/FooTestCase
+* Package - sf_root_dir/test/phpunit/fixtures/units
+* Common - sf_root_dir/test/phpunit/fixtures/common
+* Symfony - sf_root_dir/data/fixture
+
+#### File fixtures
+
+This example show you how to get path to file stored in fixture direcoties.
+
+ <?php
+
+ class FooTestCase extends sfBasePhpunitTestCase
+ implements sfPhpunitFixtureDoctrineAggregator
+ // for propel
+ //implements sfPhpunitFixturePropelAggregator
+ {
+ public function testFoo()
+ {
+ $path = $this->fixture()->getFileOwn('foo.zip');
+ // result: sf_root_dir/test/phpunit/fixtures/units/FooTestCase/foo.zip
+
+ $path = $this->fixture()->getFilePackage('foo.zip');
+ // result: sf_root_dir/test/phpunit/fixtures/units/foo.zip
+
+ $path = $this->fixture()->getFileCommon('foo.zip');
+ // result: sf_root_dir/test/phpunit/fixtures/common/foo.zip
+
+ $path = $this->fixture()->getFileSymfony('foo.zip');
+ // result: sf_root_dir/data/fixture/foo.zip
+ }
+ }
+
+If the file does not exist exception will be thrown.
+
+ <?php
+
+ class FooTestCase extends sfBasePhpunitTestCase
+ implements sfPhpunitFixtureDoctrineAggregator
+ // for propel
+ //implements sfPhpunitFixturePropelAggregator
+ {
+ public function testFoo()
+ {
+ $path = $this->fixture()->getFileOwn('not-exist-file.zip');
+ // result: exception is thrown.
+ }
+ }
+
+The same idea works for directories as well:
+
+ <?php
+
+ class FooTestCase extends sfBasePhpunitTestCase
+ implements sfPhpunitFixtureDoctrineAggregator
+ // for propel
+ //implements sfPhpunitFixturePropelAggregator
+ {
+ public function testFoo()
+ {
+ $path = $this->fixture()->getDirOwn();
+ // result: sf_root_dir/test/phpunit/fixtures/units/FooTestCase
+ }
+ }
+
+#### Database fixtures
+
+You can even load fixtures to the database. The database fixtures file is
standart propel\doctrine fixture files.
+Loads users.doctrine.yml to the database.
+
+ <?php
+
+ class FooTestCase extends sfBasePhpunitTestCase
+ implements sfPhpunitFixtureDoctrineAggregator
+ // for propel
+ //implements sfPhpunitFixturePropelAggregator
+ {
+ public function testFoo()
+ {
+ $path = $this->fixture()->loadOwn('users');
+ // load next file:
sf_root_dir/test/phpunit/fixtures/units/FooTestCase/users.doctrine.yml
+
+ $path = $this->fixture()->loadPackage('users');
+ // load next file:
sf_root_dir/test/phpunit/fixtures/units/users.doctrine.yml
+
+ $path = $this->fixture()->loadCommon('users');
+ // load next file:
sf_root_dir/test/phpunit/fixtures/common/users.doctrine.yml
+
+ $path = $this->fixture()->loadSymfony('users');
+ // load next file: sf_root_dir/data/fixture/users.doctrine.yml
+ }
+ }
+
+As you see you need not to give a file extension.
+The same for propel but extension will be *.propel.yml.
+If the file does not exist exception will be thrown.
+
+ <?php
+
+ class FooTestCase extends sfBasePhpunitTestCase
+ implements sfPhpunitFixtureDoctrineAggregator
+ // for propel
+ //implements sfPhpunitFixturePropelAggregator
+ {
+ public function testFoo()
+ {
+ $this->fixture()->loadOwn('not-exist-fixtures');
+ // result: exception is thrown.
+ }
+ }
+
+For loadOwn, loadPackage, loadCommon, loadSymfony methods plugin supports
[fluent interface](http://en.wikipedia.org/wiki/Fluent_interface)
+So you can do something like this:
+
+ <?php
+
+ class FooTestCase extends sfBasePhpunitTestCase
+ implements sfPhpunitFixtureDoctrineAggregator
+ // for propel
+ //implements sfPhpunitFixturePropelAggregator
+ {
+ public function testFoo()
+ {
+ $this->fixture()
+ ->loadOwn('users')
+ ->loadCommon('posts')
+ ->loadPackage('comments');
+ }
+ }
+
+To clean the whole database and load new fixtures:
+
+ <?php
+
+ class FooTestCase extends sfBasePhpunitTestCase
+ implements sfPhpunitFixtureDoctrineAggregator
+ // for propel
+ //implements sfPhpunitFixturePropelAggregator
+ {
+ public function testFoo()
+ {
+ $this->fixture()
+ ->clean()
+ // remove all data from the test database
+ ->loadOwn('users');
+ // only users will be in the database
+ }
+ }
+
+#### Snapshots
+
+It is very important to keep speed of the testing as fast as posible.
+Yml fixture loading is expensive operation and can slow down your testing.
+To avoid this you can use snapshot feature.
+
+And second thing where snapshots can be usefull to group some fixtures file
under single name, load all of them using this name.
+
+For example you can create some snapshots in root test suite and use them in
any test case.
+
+ <?php
+
+ class BasePhpunitTestSuite extends sfBasePhpunitTestSuite
+ implements sfPhpunitFixturePropelAggregator
+ {
+ /**
+ * Dev hook for custom "setUp" stuff
+ */
+ protected function _start()
+ {
+ $this->fixture()
+ ->clean()
+
+ // clean up tables used for storing snapshot data.
+ ->cleanSnapshots()
+
+ ->loadCommon('users')
+ ->loadCommon('categories')
+ ->loadCommon('customers')
+
+ ->doSnapshot('common');
+ }
+ }
+
+Then load the snapshot in FooTestCase for example.
+
+ <?php
+
+ class FooTestCase extends sfBasePhpunitTestCase
+ implements sfPhpunitFixtureDoctrineAggregator
+ // for propel
+ //implements sfPhpunitFixturePropelAggregator
+ {
+ public function testFoo()
+ {
+ $this->fixture()->clean()->loadSnapshot('common);
+
+ //and some additional stuff if needed.
+ $this->fixture()->loadCommon('comments');
+ // load fixtures form file
sf_root_dir/data/fixture/comments.doctrine.yml
+
+ // test code here.
+ }
+ }
+
+To clean all snapshots use method
+
+ $this->fixture()->cleanSnapshots();
+
+It is better group fixtures to snapshots and used them because it works it is
kept and loaded in mysql and so works very fast.
+
+#### Customizing fixtures
+
+Fixture object can be init with some options. like file extension and database
connection (by default it is taken from the sfContext).
+
+In future it can be done through the phpunit.yml but right now you have to
rewrite _initFixture method:
+
+ class FooTestCase extends sfBasePhpunitTestCase
+ implements sfPhpunitFixtureDoctrineAggregator
+ // for propel
+ //implements sfPhpunitFixturePropelAggregator
+ {
+ protected function _initFixture(array $options = array())
+ {
+ $options = array(
+ 'connection' => 'your connection',
+ 'fixture_ext' => 'your database fixture extension. just *.yml for
example',
+ 'snapshot-table-prefix' => 'the table prefix used for snapshots',
+ );
+
+ return parent::_initFixture($options);
+ }
+ }
+
+#### ORM objects and database fixtures.
+
+This feature allows you to get Doctrine\Propel data object by its name in
fixture file.
+For examlpe we have users.doctrine.yml file in the common fixture directory:
+
+ sfGuardUser:
+ TestUser:
+ username: user
+ password: test
+ RegistredUser2:
+ username: Lorne
+ password: Green
+ sgu_admin:
+ username: admin
+ password: test
+ is_super_admin: true
+
+And how it wotks in test case:
+
+ class FooTestCase extends sfBasePhpunitTestCase
+ implements sfPhpunitFixtureDoctrineAggregator
+ // for propel
+ //implements sfPhpunitFixturePropelAggregator
+ {
+ public function testFoo()
+ {
+ // loads users from
sf_root_dir/test/phpunit/fixtures/common/users.doctrine.yml
+ $this->fixture()->clean()->loadCommon('users);
+
+ // using method method get
+ $testUser = $this->fixture()->get('sfGuardUser_TestUser');
+ $this->assertType('sfGuardUser', $testUser);
+
+ //shorter version
+ $registeredUser = $this->fixture('sfGuardUser_RegistredUser2');
+ $this->assertType('sfGuardUser', $registeredUser);
+ }
+ }
+
+This functionallity wotks perfectly with propel. For doctrine there are some
bugs known.
+
### Custom TestCases
+This test cases are not nessesary to be used but can be helpful for you.
+
#### Selenium
+The sfBasePhpunitSeleniumTestCase class is extension of
PHPUnit_Extensions_SeleniumTestCase standart phpunit class.
+It allows you set options of a selenium server in phpunit.yml file and they
will be handled automaticaly by sfBasePhpunitSeleniumTestCase class.
+
+The selenium section of phpunit.yml config:
+
+ all:
+ selenium:
+ remote_project_dir:
+ #you can used it in case of the test application and selenium server
are run on different computers.
+ #but you have to upload the file stored in fixtures through web page.
+ #You need to mount prj dir to selenium computer and define this option.
+ #From the test case use for example method
fixture()->getDirOwnAsRemote()
+ coverage:
+ collect: false
+ coverageScript: phpunit_coverage.php
+ driver:
+ name: false
+ browser: '*firefox'
+ browser_url: false
+ host: false
+ port: false
+ timeout: false
+ http_timeout: false
+ sleep: false
+ wait: false
+
+Create your own phpunit.yml file in sf_root_dir/config for example and define
yours own options there.
+The phpunit.yml files works the same as all other symfony configs (cascad,
merging, project config dir, app config dir).
+
+The config can be look like:
+
+ all:
+ selenium:
+ driver:
+ browser: '*chrome'
+ browser_url: 'http://google.com'
+ timeout: 10
+ http_timeout: 10
+ host: selenium-server.com
+
+And test:
+
+ class GoogleTestCase extends sfBasePhpunitSeleniumTestCase
+ {
+ protected function _start()
+ {
+ //$this->setAutoStop(false);
+ $this->start();
+ $this->open('/');
+ $this->windowMaximize();
+ }
+
+ public function testGoogle()
+ {
+ // your google test here
+ }
+ }
+
#### Amf
-#### Unit
+The plugin also contains sfBasePhpunitAmfTestCase class for testing your
[AMF](http://en.wikipedia.org/wiki/Action_Message_Format) services.
+It depends on [SabreAMF](http://code.google.com/p/sabreamf/downloads/list)
library.
+So you have to be sure you install it before.
-### Custom TestSuits
+It is a kind of functional tests becuase it emulates flex client and sends
data throught the lan using http protocol.
-### Initialization
+First of you need to set a amfendpoint in phpunit.yml.
+It highly recommended to make test in the same project (run test and send
request on this server).
-### TestCase generation
+ all:
+ amf:
+ endpoint: 'http://your-server.com/amfendpoint'
+##### Send request using _getClient method:
+
+ class Amf_Service_ClientTestCase extends sfBasePhpunitAmfTestCase
+ implements sfPhpunitFixturePropelAggregator
+ {
+ public function testNewClient()
+ {
+ $service = 'Service_Test.testNewClient';
+ $params = array('test');
+
+ $response = $this->_getClient()->sendRequest($serviceName, $params);
+
+ // test response from the service
+ }
+
+##### Send request using helper class sfPhpunitAmfService.
+
+The example do same as above:
+
+ class Amf_Service_ClientTestCase extends sfBasePhpunitAmfTestCase
+ implements sfPhpunitFixturePropelAggregator
+ {
+
+ // define a service name
+ protected $_amfservice = 'Service_Test';
+
+ public function testNewClient()
+ {
+ // testNewClient is handled by __call method.
+ // It call Service_Test.testNewClient with one param 'test'
+ $response = $this->service()->testNewClient('test');
+
+ // test response from the service
+ }
+ }
+
+##### AMF Data Mapping
+
+If you use AMF data mapping this method can be very helpful for you.
+Rewrite it keeping in mind the order of array parameters:
+
+ class Amf_Service_ClientTestCase extends sfBasePhpunitAmfTestCase
+ implements sfPhpunitFixturePropelAggregator
+ {
+ public function testNewClient()
+ {
+ // your amf test
+ }
+
+ protected function _getMappedClasses()
+ {
+ // array('flex_class' => 'php_class');
+ return array('ClientFlex' => 'ClientPHP');
+ }
+ }
+
+### Stubs data
+
+The sfBasePhpunitAmfTestCase class contain helpful method getStub.
+It can be used only for stubs object.
+The method has the same interface as getMock method.
+
+This method allows you to create stubs objects simplier then getMock method:
+
+Compare this two ways:
+
+ <?php
+
+ class FooTestCase extends sfBasePhpunitTestCase
+ {
+ public function testFoo()
+ {
+ $stub = $this->getMock('DataManager', array('getFoo', 'getBar'));
+
$stub->expects($this->any())->method('getFoo')->will($this->returnValue('foo'));
+
$stub->expects($this->any())->method('getBar')->will($this->returnValue('bar'));
+
+ // test code here
+ }
+ }
+
+and:
+
+ <?php
+
+ class FooTestCase extends sfBasePhpunitTestCase
+ {
+ public function testFoo()
+ {
+ $stub = $this->getStub('DataManager', array(
+ 'getFoo' => 'foo',
+ 'getBar' => 'bar'));
+
+ // test code here
+ }
+ }
+
+getStub method used $this->any() method to set numbers of the stubed methods
called.
+You can use more strict version of this method:
+
+ $this->getStubStrict( ... );
+
+This method used $this->atLeastOnce(),
+
+If you need create two stubs with cross referance to each other. The next way
can help:
+
+ <?php
+
+ class FooTestCase extends sfBasePhpunitTestCase
+ {
+ public function testFoo()
+ {
+ $stubFoo = $this->getStub('Foo', array('getBar' =>
$this->stubLater()));
+ $stubBar = $this->getStub('Bar', array('getFoo' => $stubFoo));
+
+ $stubFoo->expects($this->any())->method('getBar' => $stubBar);
+
+ // test code here
+ }
+ }
+
+### Good practices
+
+#### Use a diferent database for testing and dev (prod the same idea):
+
+ test:
+ doctrine:
+ param:
+ dsn: mysql:host=127.0.0.1;dbname=project_test
+ #dsn: sqlite:////file_to/test.db?mode=0666
+ #dsn: "sqlite::memory:"
+
+ all:
+ doctrine:
+ class: sfDoctrineDatabase
+ param:
+ dsn: mysql:host=127.0.0.1;dbname=project
+ username: mysqluser
+ password: mysqlpass
+
### Plans
* Finish DbUnit fixture
* fix autoloading bug in AllTests.php
* several drivers for selenium
* form testing
-* fix retriving objects by fixture name
+* fix retriving objects by fixture name (doctrine)
* More inteligent init task
+* move models directory to units one
+* fixture options to phpunit.yml config.
+* standart phpunit config in phpunit.yml.
+* fresh sfConfig for each test case.
### Feedback
\ No newline at end of file
--
You received this message because you are subscribed to the Google Groups
"symfony SVN" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/symfony-svn?hl=en.