When developing symfony plugins for use and reuse in your projects and maybe (hopefully) by the symfony community you want to make sure that a new release doesn't break with backwards compatibility. While symfony comes with lime the de facto standard for unit testing in PHP is PHPUnit and it offers much more. This session will show you some of the best practices of testing symfony plugins. You will learn how to organise your tests and how to reduce your dependencies to the bare essentials.
2. Christian Schäfer: Unit testing von symfony plugins with PHPUnit
About myself
●
nick: caefer
●
34, married, two cats
●
senior system architect at Gruner+Jahr in hamburg
●
web developer for over 11 years
●
symfony developer for over 3 years
●
blogs daily on http://test.ical.ly
●
tweets @testically
●
experiments on github.com/caefer
●
loves scottish whisky, british comedy and uncommon music
and his wife and two cats obviously :)
3. Christian Schäfer: Unit testing von symfony plugins with PHPUnit
What is your motivation?
• What is your main interest when developing plugins?
• Do you unit test your plugins?
• Who has worked with PHPUnit before?
4. Christian Schäfer: Unit testing von symfony plugins with PHPUnit
Defining the goals
When developing plugins you want to make sure they..
• ..can be used stand-alone
• ..can be installed easily
• ..hold as little dependencies as possible*
• ..work!
* where dependencies to symfony itself and Doctrine or Propel are only natural
5. Christian Schäfer: Unit testing von symfony plugins with PHPUnit
Prerequisites
In this talk I will be using the following:
• symfony 1.4
• PHPUnit 3.5
• Hudson CI
• sfTaskExtraPlugin
Always use sfTaskExtraPlugin when creating a new plugin!
6. Christian Schäfer: Unit testing von symfony plugins with PHPUnit
A word about unit testing in symfony 1
lime works! ..but
• Not too well documented
• Not found outside symfony
• Not easy to integrate in continuous integration servers
Why I prefer PHPUnit instead
• It's well documented
• It's well distributed
• It's the de factor standard in the PHP world
• It's actively maintained
• It can easily be integrated in Hudson, phpUnderControl,
Bamboo, yourFavouriteCISoftware
7. Christian Schäfer: Unit testing von symfony plugins with PHPUnit
Getting started
Did I mention to use sfTaskExtraPlugin ?
Create a plugin with this command:
$ php symfony generate:plugin csTicTacToePlugin
Then take a look at the files that were created.
Two things are of particular interest:
• test/fixtures/project/*
• test/bootstrap/unit.php
8. Christian Schäfer: Unit testing von symfony plugins with PHPUnit
The fixture project
The fixture project is a sandbox, a testing environment for your plugin.
• It is a fully working symfony project.
• You can even point a vHost to its web folder! (don't!)
• You can run symfony commands in it.*
• Add/remove/modify anything in it that your plugin requires of any
project!
• Document everything you change.
* beforehand: $ export SYMFONY=/path/to/your/symfony/lib/dir
9. Christian Schäfer: Unit testing von symfony plugins with PHPUnit
The fixture project - todos
• Configure a database connection
dsn: sqlite::memory:
• Generate your models
$ php symfony doctrine:build allclasses
• Enable modules if exist
• Create test fixtures in data/fixtures/
• Create local schema.yml if required for testing (i.e. for behaviours)
• Enable plugins if required by your plugin
10. Christian Schäfer: Unit testing von symfony plugins with PHPUnit
The bootstrap
The bootstrapping process is responsible for firing up symfony and
all that is required for your tests.
This can include
• Autoloading symfony
• Enable required plugins (i.e. your own)
• Autoloading project specific classes (i.e. model classes)
• Database configuration
• Rest of the configuration cascade
11. Christian Schäfer: Unit testing von symfony plugins with PHPUnit
The bootstrap - todos
• Remove lime
require_once $configuration>getSymfonyLibDir().'/vendor/lime/lime.php';
• Adjust project configuration to your needs
$projectPath = dirname(__FILE__).'/../fixtures/project';
1. Generic project config → symfony paths, plugins, project autoloading, database, configuration cascade
$configuration = new sfProjectConfiguration($projectPath);
2. Fixture project config → symfony paths, plugins, project autoloading, database, configuration cascade
require_once($projectPath.'/config/ProjectConfiguration.class.php');
$configuration = new ProjectConfiguration($projectPath);
3. Fixture application config → symfony paths, plugins, project autoloading, database, configuration cascade
require_once($projectPath.'/config/ProjectConfiguration.class.php');
$configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'test', true);
12. Christian Schäfer: Unit testing von symfony plugins with PHPUnit
Pre-configuring PHPUnit
With the 3.5 release using PHPUnit became much more convenient!
Create a phpunit.xml.dist for a default configuration and ship it
with your plugin. Users can create their own phpunit.xml which will
be prefered by PHPUnit if it exists.
• 1. Configure bootstrap
• 2. Configure the path to the symfony lib dir
• 3. Configure the path to the unit tests
• 4. Configure scope for the coverage report
13. Christian Schäfer: Unit testing von symfony plugins with PHPUnit
Pre-configuring PHPUnit - todos
1. Configure bootstrap
<phpunit … bootstrap="test/bootstrap/unit.php" … >
2. Configure the path to the symfony lib dir
<php><server name="SYMFONY" value="../symfony/"/></php>
3. Configure the path to the unit tests
<testsuites>
<testsuite name="csTicTacToePlugin Suite">
<directory>./test/unit/</directory>
</testsuite>
</testsuites>
4. Configure scope for the coverage report
<filter>
<whitelist addUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./lib/</directory>
<directory suffix=".php">./config/</directory>
</whitelist>
</filter>
14. Christian Schäfer: Unit testing von symfony plugins with PHPUnit
Using the database
Always initialize the database in your TestCases setUp() method!*
• Initialize the database manager
new sfDatabaseManager(ProjectConfiguration::getActive());
• Create database
Doctrine_Manager::getInstance()>createDatabases('doctrine');
• Create all tables
Doctrine_Core::createTablesFromModels(sfConfig::get('sf_lib_dir'));
• Create specific tables
Doctrine_Core::createTablesFromArray(array('SomeRecord', ..));
• Load fixtures
Doctrine_Core::loadData(sfConfig::get('sf_data_dir').'/fixtures/fixtures.
yml')
* This is Doctrine specific! Unfortunately I don't know how to do the following with Propel..
15. Christian Schäfer: Unit testing von symfony plugins with PHPUnit
Lets see an example
Here's a simple plugin for demonstration:
http://github.com/caefer/csTicTacToePlugin
Here are a few of my best practices:
• Test every class in your config and lib folder.
• Write one test case per class.
• Mirror the directory structure in your test/unit folder.
• Name you test case like the covered class postfixed with “Test”.
config/csTicTacToePluginConfiguration.class.php
lib/model/doctrine/PluginTicTacToe.class.php
→ test/unit/config/csTicTacToePluginConfiguration.class.php
→ test/unit/lib/model/doctrine/PluginTicTacToe.class.php