Send the link below via email or IMCopy
Present to your audienceStart remote presentation
- Invited audience members will follow you as you navigate and present
- People invited to a presentation do not need a Prezi account
- This link expires 10 minutes after you close the presentation
- A maximum of 30 users can follow your presentation
- Learn more about this feature in our knowledge base article
PHP Unit Testing: a practical approach
Transcript of PHP Unit Testing: a practical approach
PHP Unit Testing: a Practical Approach
or: How I Learned to Stop Worrying and Love them
28 years old
Unit Testing: protecting your work from yourself
Even good programmers make mistakes. The difference between a good programmer and a bad programmer is that the good programmer uses tests to detect his mistakes as soon as possible. The sooner you test for a mistake the greater your chance of finding it and the less it will cost to find and fix. This explains why leaving testing until just before releasing software is so problematic. Most errors do not get caught at all, and the cost of fixing the ones you do catch is so high that you have to perform triage with the errors because you just cannot afford to fix them all.
-- phpunit online introduction
Improve your code quality
Detect mistakes in earlier stages
Force yourself to think about every possible scenario
Allow you to safely refactor your code
Lower the chances of carrying bugs inside releases
Avoid velociraptor attacks
A small/medium amount of rectoring is needed
You'll become the "trouble maker" guy
Setting them up is not so easy, especially when working with frameworks
Errors are not tolerated: your tests will become the holders of the truth
If a function is made up of several sub-functions, you have to test them all before being sure that your test is really working
√Are very boring to write
Adding tests to Framework On Framework library
Say Hello to PhIL!
Creating the architecture
Where do I start?
Configure your workstation
Creating a local copy of Joomla!
The config.php file
Creating the first test!
The class to test
Things are getting harder....
Adding database support
Dealing with singletons and internal cache
Debugging your tests
Dealing with native functions
Adding Travis CI integration
Testing more useful methods
The class to test
What's Travis CI?
Configuring your repository
A more useful .travis.yml file
Configure Travis: a path made of tests and trials
Changing the bootstrap file
Framework On Framework :
PhpUnit website :
Travis CI website :
FrameworkOnFramework library was growing quite fast and we started experiencing some regression issues.
Moreover, FoF was going to be added inside Joomla! installation package and we had to provide Unit Testing in order to satisfy the requirements.
We had been discussing about Unit Testing since January 2013, but we've never started anything.
Then, after watching Javier session on this topic at Joomla! and Beyond, we decided that we really need them.
Guess who was assigned to the ticket?
Developing Unit Tests for a library is quite different than developing them for an extension. There are several factors to consider:
testing vs different Joomla! versions
testing vs different PHP versions
testing internal methods
dealing with library optimizations (singletons, internal chace etc. etc.)
heavy database usage
automatic tests on contributions
interaction with Joomla! environment
A very powerful Integrated Development Environment;
Do not try to write down Unit Tests without an IDE!
debug plus a lot of different cool features: Phing, LESS compiler, CodeSniffer etc. etc.
it supports out the box Unit Testing and advanced
Unit Tests will be performed using PhpUnit library; it can be installed in several ways:
chmod +x phpunit.phar
mv phpunit.phar /usr/local/bin/phpunit
pear config-set auto_discover 1
pear install pear.phpunit.de/PHPUnit
pear install phpunit/DbUnit
Testing internal methods
Travis CI will bring automatic tests inside your GitHub repository: every time a pull request is created, tests are run.
This is very useful for everyday work, but in our case it has been fundamental since everyone can create a pull request and we can't force users to setup tests on their workstations.
Moreover, using Travis we can test FoF versus different PHP and Joomla! versions.
Finding practical info on how to write Unit Tests was the most difficult part.
There are lot of guides about the topic, but none of them were completely accurate when dealing with frameworks like Joomla!
Using Joomla! unit tests hasn't been very helpful, since they are very complicated and hard to understand for a novice tester.
Example of configuration file. You have to copy it and change the values with your workstation ones
Real configuration file that will be used during the tests
Special configuration file loaded when we're running tests using Travis CI
This folder contains Joomla! versions.
In addition to contain required libraries, there
are a ready-to-use configuration file and the sql installation file, in order to setup the database schema for Joomla! related tables.
Under the core folder we can find the base classes for tests (normal and database one), plus a set of ready-to-use mocks.
The dbtest.sql file will create the requested tables for our tests.
At the moment we are creating four different tables:
Inside the stubs folder we have the files that will actually load data inside the database.
The dbimport.php file will create the database schema, while the xml file contains the rows for every table.
Here we can find all the tests.
Tests are organized in directory and follow this convention:
`- <file name>
`- <file name>Test.php
`- [<file name>Dataprovider.php]
Simply install Joomla! on your local workstation. There aren't any "special" settings, you just have to remember to use
as your table prefix.
Here you simply have to supply the path to your Joomla! installation:
The test base class
Choose your db prefix wisely
PhpUnit will read the contents and will TRUNCATE the tables, then INSERT the data inside them before every test.
Data used for tests is stored inside an XML file.
Since we can't use the #__ convention prefix, we chose to use the old default one: jos_
Speeding up your tests
Test classes that extend the PhpUnit abstract one will ALWAYS truncate and then restore data inside your tables, regardless of the operations performed.
Can we avoid this task for tests that don't touch the database?
Sometimes we have to use PHP native functions (ie
in order to create random values or check the environment dynamically.
Sadly, there is no way to mock these functions, the only solution is to wrap them inside a platform-wide function, so we will be able to mock them.
Almost every method inside FoF has an onBefore / onAfter pair, so developers can easily override them.
This means that when we're testing the function
its result depends on
How can I test my main function, considering all possible outputs?
PhpUnit mocking feature
Inside PhpUnit we can "mock" an object, forcing one or more of its methods to return a fixed, known, value.
Singletons and internal cache are a must-have in production environments, but are a pain to test!
We always need a new fresh object every time a test is run, moreover almost every time we have to "inject" our custom class (ie mocked objects).
How can we force the framework to use the objects we need?
Behaviors we need to test:
publishing and unpublishing behavior
if the table has publishing support
if we actually have any IDs
vs single and multiple IDs
return value of the method
if data was actually saved on db
vs table already loaded (and no incoming ID)
if the onBeforePublishing event went fine
honor the checkin / checkout logic
vs table with alias for the `enabled` column
URL to this presentation :
Travis is a tool that will "watch" your repository, running tests every time a new pull request is made.
In this way you can safely accept work from other developers without forcing them to setup a testing environment.
Debug Travis CI
Travis will set several environment variables, that we can use to detect him and load the correct configuration.
Remember to create a database with the correct name and permissions for the user
Creating a new configuration profile (1)
Creating a new configuration profile (2)
Creating a new configuration profile (3)