SlideShare a Scribd company logo
1 of 43
Download to read offline
TRAINING COURSE PHPUNIT
Training Course PHPUnit
Training Course PHPUnit Nick Belhomme 2010 p. 1
TRAINING COURSE PHPUNIT
Goal of this course
We will introduce you into the art of unit testing.
What is Unit testing and how does it work. Together we will build a testing suite for a domain
model (computer text based game) and finally implement this model into Zend Framework 1.11 and
up. Making you comfortable with the PHPUnit testing framework.
Training Course PHPUnit Nick Belhomme 2010 p. 2
TRAINING COURSE PHPUNIT
What where how?
Training Course PHPUnit Nick Belhomme 2010 p. 3
TRAINING COURSE PHPUNIT
What is Unit Testing?
Unit Testing is the testing of units. The tests are done to ensure that each unit is working like it is
intended to work.
The tests should be automatic and performed on regular intervals performing validation and
verification on the correct working of the units tested. Ideally each test should be independent of
another. A unit to be tested is a small piece of software code. Today in Object oriented programming
languages these small pieces of software code are generally individual methods from a specific
class. Unit testing gives us a way to test our implementations, design and behavior on the classes we
write.
Today we can claim that Unit testing is a fundamental part of quality modern software development.
Benefits of Unit Testing
Unit testing has three main benefits:
• It makes you think about your application and about the implementation route you have
chosen / are about to chose instead of running off half cocked implementing bad design
decisions.
• It gives an immediate view on the proper functioning of the system and facilitates locating a
defect in the system.
• It facilitates the refactoring of existing code. Which is equally important to guarantee the
longevity of an application.
"Refactoring is the process of changing a software system in such a way that it does not
alter the external behavior of the code yet improves its internal structure. It is a disciplined
way to clean up code that minimizes the chances of introducing bugs. [Preface Refactoring
Martin Fowler]"
When something breaks the application because of external factors (ie. DB credential changes) or
because changes made to the code, Unit Tests will quickly show you were the error or bug has been
introduced. Allowing you to quickly locate and fix the bug. In the end speeding up development
time.
Because of the tests in place you as a developer will feel more confident in your code and in your
future changes. Reflecting that confidence to customers, management and other developers.
Training Course PHPUnit Nick Belhomme 2010 p. 4
TRAINING COURSE PHPUNIT
Testing pieces of software
Mostly for (PHP) developers testing their code was programming something and displaying it in the
browser. If it worked we moved on to the next piece of software to implement.
In a modern development cycle unit testing is a core component. Instead of testing stuff in the
browser we also test by automating different scenarios.
“In general if you think you need a var_dump() for testing your code, you are better of making that
test eternally embedded in your test-suite. Martin-Fowler”
Every programmer makes mistakes. What differentiates the good from the bad is that the good
programmer uses tests to detect his mistakes ASAP. Reducing debugging costs to a minimum and
making the product more stable.
Okay up to our first test. We are going to test the CAST operator
<?php
$fixture = new stdClass();
$fixture->id = 1248;
$fixture->name = 'Robin Hood';
// $fixture is expected to be an object from the type stdClass
// with two properties set
$fixture = (array) $fixture;
// $fixture is expected to be an array
// with two key value pairs set
If we want to check if the behavior of the (array) cast operator is working as intended we can add a
simple check
<?php
$fixture = new stdClass();
$fixture->id = 1248;
$fixture->name = 'Robin Hood';
echo is_object($fixture), PHP_EOL;
echo $fixture->id == 1248, PHP_EOL;
echo $fixture->name == 'Robin Hood', PHP_EOL;
$fixture = (array) $fixture;
echo is_array($fixture),PHP_EOL;
echo $fixture['id'] == 1248, PHP_EOL;
echo $fixture['name']== 'Robin Hood', PHP_EOL;
Training Course PHPUnit Nick Belhomme 2010 p. 5
TRAINING COURSE PHPUNIT
Everything is working as expected because we get for the 6 tests all 1
But at this time the tests are not automated and still require a human to check if every test has
returned 1.
Scanning over a big list of “ones” could get tiring and frustrating if you have to do it several times a
day.
We should automate this so that the tests only report unwanted behavior.
Let's improve this.
<?php
$fixture = new stdClass();
$fixture->id = 1248;
$fixture->name = 'Robin Hood';
assertTrue(is_object($fixture));
assertTrue($fixture->id == 1248);
assertTrue($fixture->name == 'Robin Hood');
$fixture = (array) $fixture;
assertTrue(is_array($fixture));
assertTrue($fixture['id'] == 1248);
assertTrue($fixture['name']== 'Robin Hood');
function assertTrue($condition)
{
if (!$condition) {
throw new Exception('Assertion failed.');
}
}
The tests are now fully automated and only report when something fails.
There are several test suites readily available to make life easier for you as a developer. These test
suites have the functions like assertTrue already implemented amongst many others.
This course is on the topic on the XUnit family of testing frameworks and more specifically
PHPUnit.
Training Course PHPUnit Nick Belhomme 2010 p. 6
TRAINING COURSE PHPUNIT
PHPUnit as a part of the XUnit Family
The XUnit family is a collective of various code-driven testing frameworks. They provide an
automated solution with no need to write the same tests many times, and no need to
remember what the result should be of each test. The name XUnit is a derivation of JUnit the first to
be widely known.
Each programming language has his own XUnit framework(s).
The two most famous Unit testing frameworks for PHP are SimpleTest by Marcus Baker and
PHPUnit by Sebastian Bergmann.
In this course we have chosen to elaborate PHPUnit. The reason is because it is more widely
adopted for PHP. Which is reflected by the adoption for it in major PHP frameworks like Zend
Framework and Symfony. Another reason is SimpleTest is not really maintained anymore.
When to create Unit tests
When you have classes which aren't covered yet by tests you should start to setup a testing suite to
make sure your code gets validated and checked on a regular interval. If you put those tests in place
you will become more confident in adding extra functionality and refactoring your existing code
base.
You could follow this methodology. Writing first the classes and then the tests. While this is better
than writing no tests at all it has some drawbacks:
• Some tests will not get written.
• Not that much thought will be put in the initial implementation. And putting initial thought
into your implementation is a key principal for every good programmer.
If we analyse the previous thought cycle we come to the conclusion that it is a wise decision to put
first the tests in place and then the implementation. This is exactly what Test Driven Development
TDD is all about. It dictates that all tests should be written prior to implementing the code.
TDD is a derivation from Xtreme Programming and it makes much sense to develop this way.
When we develop an application we should think about it before implementing it. TDD forces this
path. And puts tests in place before even implementing a single piece of application code.
But you shouldn't think in terms of tests but in terms of behavior. This brings us to Behavior Driven
Development. Which has the benefits of TDD but shifts the focus.
“So if it's not about testing, what's it about?
It's about figuring out what you are trying to do before you run off half-cocked to try to do it. You
write a specification that nails down a small aspect of behaviour in a concise, unambiguous, and
executable form. It's that simple. Does that mean you write tests? No. It means you write
specifications of what your code will have to do. It means you specify the behaviour of your code
ahead of time. But not far ahead of time. In fact, just before you write the code is best because that's
when you have as much information at hand as you will up to that point. Like well done TDD,
Training Course PHPUnit Nick Belhomme 2010 p. 7
TRAINING COURSE PHPUNIT
you work in tiny increments... specifying one small aspect of behaviour at a time, then implementing
it. When you realize that it's all about specifying behaviour and not writing tests, your point of view
shifts. Suddenly the idea of having a Test class for each of your production classes is ridiculously
limiting. And the thought of testing each of your methods with its own test method (in a 1-1
relationship) will be laughable. —Dave Astels
.
Training Course PHPUnit Nick Belhomme 2010 p. 8
TRAINING COURSE PHPUNIT
Getting Started
Training Course PHPUnit Nick Belhomme 2010 p. 9
TRAINING COURSE PHPUNIT
Installing PHPUnit
Installing PHPUnit is straight forward. You use the Pear Installer, set up your path (which should
already be done for PEAR) and that's it.
1. If you have PEAR already installed skip to step 2.
go to your phpPEAR directory and do:
WINDOWS:
php phar.require_hash=0 go-pear.phar
register the PEAR path to your system environment:
right click "this computer" => "properties" => "advanced" => "environment variables".
In the field "system variables" select PATH, "edit" and add the full path to PEAR to
the end of the list.
*NIX:
php go-pear.phar
register the PEAR path to your system environment
2. update your local PEAR environment:
pear channel-discover pear.phpunit.de
3. install PHPUnit
pear install phpunit/PHPUnit
Defining the project
We are going to build a text based adventure game. The setup of the game will be much alike the
old adventure games of Sierra and Lucas Arts but then purely text based.
We will have a character (you) which can travel from location to location, picking up objects,
manipulating the objects and talking to characters.
So we will need:
Grid : the gaming world holding all of the locations. We should be able to change position on the
grid.
Training Course PHPUnit Nick Belhomme 2010 p. 10
TRAINING COURSE PHPUNIT
Tile: a location on the grid. This can be a room or for instance a jungle. We should be able to
perform various actions on it and store objects in it.
Item: an item on which various actions can be added in order to manipulate it. Also possibility to
talk to the item.
ItemCombination: Defines which two items can be used together.
Action: an action, manipulation.
Inventory: a list of items.
Conversation: A conversation which can be linked to an item.
All this classes will have their own prefix Game_ which will serve as a name space.
It is always good practice to define each project and library with a specific name space / prefix. This
way same name clashes will most likely not occur.
Training Course PHPUnit Nick Belhomme 2010 p. 11
TRAINING COURSE PHPUNIT
Writing your first test case: Implementing the Grid
First requirement is we will have to create a class called Game_Grid. Let's define this in a unit test.
Tests/PHPUnit/Library/Game/GridTest.php
<?php
require_once 'PHPUnit/Framework.php';
require_once '../../../../Library/Game/Grid.php';
class Game_GridTest extends PHPUnit_Framework_TestCase
{
public function testConstructor()
{
$grid = new Game_Grid();
}
}
running the test will result a FATAL ERROR!
Training Course PHPUnit Nick Belhomme 2010 p. 12
TRAINING COURSE PHPUNIT
This is because we didn't create the Game/Grid.php file yet OR we haven't setup our include path
correctly OR the file Grid.php doesn't include a class of the type Game_Grid. Which can't be auto
loaded.
Once we have fixed these possible reasons we do not receive the fatal error anymore.
Instead we get OK (1 test, 0 assertions)
Let us analyze the unit test code.
First we include our PHPUnit framework and our class to test with require statements.
All Unit test cases are in the [class name]Test format. You take the class name to test and append it
with Test.
The class extends PHPUnit_Framework_TestCase which will give you access to the full power of
Training Course PHPUnit Nick Belhomme 2010 p. 13
TRAINING COURSE PHPUNIT
PHPUnit. This includes various template methods such as setUp() and tearDown(), assertion methods,
mocking, etc.
We let PHPUnit find every test by sticking to the naming format that every function starting with
the string test, is a test.
So every test method starts with test appended with a string of choice.
It is best to give a descriptive method name explaining what you are testing.
In example: public function testGetDescriptionToReturnFalseOnInitialisation()
PHPUnit finds all the tests by reflection. It searches for all methods of the format test* and will
execute them in the order defined. You should make sure every test is independent of another. And
finally all tests can have multiple assertions. In our test we didn't include any assertions yet but they
will grow rapidly.
So what did we do to fix the bug?
1) We first create our application structure
It is good practice to separate the tests and the actual library into two distinct folders.
We create a sub folder in Tests because several testing suites can be available for an
application, PHPUnit is one of them. The test case classes in the Tests/PHPUnit directory
(will) mirror the package and class structure of the System Under Test (SUT).
Probably the easiest way to compose a test suite is to keep all test case source files in a test
directory. PHPUnit can automatically discover and run the tests by recursively traversing the
Training Course PHPUnit Nick Belhomme 2010 p. 14
TRAINING COURSE PHPUNIT
test directory.
2) Then we start by creating our unit test
We created our first test called GridTest.php with the above Unit test embedded.
All test cases respect the following file naming convention:
[class-name-to-test] +“Test.php”
3) We execute the command: "phpunit GridTest.php" in the folder where the test is located.
This will result in a fatal error: require_once...
4) We add the file Grid.php in the Library directory.
5) Re-run the test again and we will see it fail once more: Class not found
Training Course PHPUnit Nick Belhomme 2010 p. 15
If you get an error that your operating system doesn't recognize phpunit, you
haven't set your execution rights or the path environment variables correct.
After installing PHPUnit you should be able to execute the phpunit command
from everywhere in the file system.
TRAINING COURSE PHPUNIT
6) Add the class definition into the Grid.php file.
Library/Game/Grid.php
<?php
class Game_Grid {}
7) run the test once more and see it pass.
Bootstrapping PHPUnit test cases
Because a lot of basic functions are repetitive and should be loaded for each test we could bootstrap
it. In the bootstrap we can for instance set the include path, or an auto loader. Or any other directive
that your application needs for running correctly. You can load a bootstrap file by passing it as a
parameter
PHPUnit --bootstrap [bootstrap file] [test case to run]
in example: PHPUnit --bootstrap Bootstrap.php GridTest.php
In our test suite we will be bootstrapping our tests. This will include a basic auto loader so we do
not have to write all the includes all the time. When we run our application itself we will also auto
load it. Everything will be auto loaded in the application. So why should our unit tests be any
different.
Create a file called Bootstrap.php (you can call it whatever you want but it is always a good idea to
stick to naming conventions. Because PHPUnit regards it as a bootstrap, you might as well name it
Training Course PHPUnit Nick Belhomme 2010 p. 16
TRAINING COURSE PHPUNIT
that way).
In the bootstrap we make sure our PHPUnit framework is loaded and implement a simple auto
loader.
<?php
require_once 'PHPUnit/Framework.php';
function __autoload($className)
{
$path = explode('_', $className);
$root = '';
if ($path[0] == 'App') {
$root = '../../../../Application/Library/';
array_shift($path);
} else if ($path[0] == 'Game') {
$root = '../../../../Library/Game/';
array_shift($path);
}
require_once $root.implode(DIRECTORY_SEPARATOR, $path).'.php';
}
Now that we have the auto loader in place we can remove the require_once from our tests.
Training Course PHPUnit Nick Belhomme 2010 p. 17
TRAINING COURSE PHPUNIT
Configure PHPUnit with a phpunit.xml configuration file
You can pass a lot of optional parameters to PHPUnit. For a complete list run the command without
any test to run, any parameter or with the parameter --help
PHPUnit
PHPUnit --help
We can use a configuration file to automagically pass parameters to the phpunit command.
You can define which bootstrap to load and where to find it. Which tests should be run etc.
PHPUnit will by default search for a file called phpunit.xml in the directory you run the command
from. Of course you can name it any other file name. But we suggest you sticking to that. If you
need multiple configuration files you can name them whatever you want.
In example:
phpunit.xml for normal testing
phpunitCruisecontrol.xml for a special cruisecontrol setup which is a part of the continuous
integration methodology.
Let's take a closer look at the configuration file we will setup:
Training Course PHPUnit Nick Belhomme 2010 p. 18
TRAINING COURSE PHPUNIT
Tests/PHPUnit/Library/Game/phpunit.xml
<?xml version="1.0" encoding="utf-8"?>
<phpunit bootstrap="./Bootstrap.php">
<testsuite name="Game">
<directory>./</directory>
</testsuite>
<logging>
<log type="coverage-html" target="/tmp/PHPUnit/Coverage/"
charset="UTF-8" yui="true", highlight="false" lowUpperBound="35"
highLowerBound="70" />
</logging>
</phpunit>
This will tell PHPUnit you want the Bootstrap.php loaded as a bootstrap and the entire test suite
called Game Test Suite is in the current directory and subfolders.
Alternatively instead of setting a <directory> you can also specifically set the order using the <file>
tag. The order in which you define the <file> tag is the order in which the tests will be executed.
After specifying these configurations PHPUnit can find all tests automatically and you do not have
to write PHPUnit --bootstrap Bootstrap.php GridTest.php anymore.
The following command will suffice:
PHPUnit
Training Course PHPUnit Nick Belhomme 2010 p. 19
You do not have to specify the XML declaration for PHPUnit to understand it.
But as always it is best practice.
TRAINING COURSE PHPUNIT
Training Course PHPUnit Nick Belhomme 2010 p. 20
TRAINING COURSE PHPUNIT
Writing Assertions
We want to define the grid as a square which can hold Tiles. The Grid should be dynamic in size,
take actions and store a position indicating where you are on the map.
Let's first start implementing the constructor. We want it to accept the number of vertical tiles and
horizontal tiles as integers. Then we want to be able to get the grid size set.
We define the requirements by means of a Test.
<?php
public function testConstructorNormalParamsWithGetGridSize()
{
$x = rand(1,100);
$y = rand(1,100);
$grid = new Game_Grid($x,$y);
$gridSize = $grid->getGridSize();
$this->assertEquals($x, $gridSize['x']);
$this->assertEquals($y, $gridSize['y']);
$grid = new Game_Grid((string) $x, (string) $y);
$gridSize = $grid->getGridSize();
$this->assertEquals($x, $gridSize['x']);
$this->assertEquals($y, $gridSize['y']);
}
We run the test: PHPUnit
As you can see there is a fatal error: call to undefined method.
Lets implement these requirements into Game_Class
Training Course PHPUnit Nick Belhomme 2010 p. 21
TRAINING COURSE PHPUNIT
<?php
class Game_Grid
{
protected $_grid;
public function __construct($gridSizeX, $gridSizeY)
{
for ($x = 0; $x < (int) $gridSizeX; $x++) {
for ($y = 0; $y < (int) $gridSizeY; $y++) {
$this->_grid[$x][$y] = null;
}
}
}
public function getGridSize()
{
return array('x' => count($this->_grid), 'y' => count($this->_grid[0]));
}
}
Testing for Exceptions and PHP Errors
Okay so far so good. But what happens if we pass negative values or no integers at all? Time to put
it to a test and make sure how code can handle these kind of situations.
This is one of the fundamental rules in unit testing: check boundaries or abnormal situations. By
thinking of ways your application might be used and putting it thru the test, you develop more
robust and safer software. It also makes you understand your code and the requirements better.
1. Test for boundary conditions.
2. Test for both success and failure.
Training Course PHPUnit Nick Belhomme 2010 p. 22
TRAINING COURSE PHPUNIT
3. Test for general functionality.
So let us extend the testCase with new tests and some more assertions.
public function testConstructZeroParamsX()
{
$this->setExpectedException('Exception');
$grid = new Game_Grid(0,3);
}
public function testConstructZeroParamsY()
{
$this->setExpectedException('Exception');
$grid = new Game_Grid(2,0);
}
public function testConstructNegativeParamsX()
{
$this->setExpectedException('Exception');
$grid = new Game_Grid(-2,3);
}
/**
* @expectedException Exception
*/
public function testConstructNegativeParamsY()
{
$grid = new Game_Grid(2,-3);
}
Here we define the requirement to throw an Exception when a negative parameter is given.
You can tell the testing framework to expect an exception in two ways.
* By setting a DocBlock with @expectedException [exception to expect]
* By calling the setExpectedException($exceptionToExpect) method on the framework
Both work equally well but you should choose one. Remember consistency makes reading much
more comprehensible.
The test will show us that this is not yet thrown by our implementation
Training Course PHPUnit Nick Belhomme 2010 p. 23
TRAINING COURSE PHPUNIT
This will force us to write the following implementation:
public function __construct($gridSizeX, $gridSizeY)
{
if ($gridSizeX <= 0 || $gridSizeY <= 0) {
throw new Exception('The size of the Grid cannot be negative or zero');
}
for ($x = 0; $x < (int) $gridSizeX; $x++) {
for ($y = 0; $y < (int) $gridSizeY; $y++) {
$this->_grid[$x][$y] = null;
}
}
}
Training Course PHPUnit Nick Belhomme 2010 p. 24
You can also test on PHP warnings for instance when you do type hinting for
parameter checking. PHP will throw a warning when you define a function
which expects a certain type but you provide another type.
You can test that functionality with:
$this->setExpectedException('PHPUnit_Framework_Error');.
This is useful when you want your application to force the user to use a specific
type. You can check if you have put this check in place.
TRAINING COURSE PHPUNIT
This time the test will succeed and you can move on to the next assertion you can think of.
There are a lot of assertions to choose from.
A complete list can be fount here: http://www.phpunit.de/manual/current/en/api.html#api.assert
Training Course PHPUnit Nick Belhomme 2010 p. 25
TRAINING COURSE PHPUNIT
Incomplete and skipped tests
Generally when you are working on a new test case class, you might want to begin by writing
empty test methods for your public api such as:
public function testGetTileWhenNoneIsSet()
{
$this->markTestIncomplete( 'This test has not been implemented yet.' );
}
public function testGetTileFromPostionWhenNoTileIsSet()
{
$this->markTestIncomplete( 'This is a custom message.' );
}
public function testAddTile()
{
$this->markTestIncomplete( 'This test has not been implemented yet.' );
}
public function testAddTileOutsideGrid()
{
$this->markTestIncomplete( 'This test has not been implemented yet.' );
}
public function testIsOnGrid()
{
$this->markTestIncomplete( 'This test has not been implemented yet.' );
}
public function testPosition()
{
$this->markTestIncomplete( 'This test has not been implemented yet.' );
}
public function testSetPositionOutsideGrid()
{
$this->markTestIncomplete( 'This test has not been implemented yet.' );
}
public function testDefaultGridActions()
{
$this->markTestIncomplete( 'This test has not been implemented yet.' );
}
public function testAddActionAndGetActions()
{
$this->markTestIncomplete( 'This test has not been implemented yet.' );
}
This practice will make sure you do not forget to test some of your public api methods. PHPUnit
will tell you on each run you did not yet implement the required test.
Training Course PHPUnit Nick Belhomme 2010 p. 26
TRAINING COURSE PHPUNIT
Having an empty tests will not suffice.
public function testSomething()
{
}
Empty tests have the problem that they are interpreted as a success by the PHPUnit framework.
This misinterpretation leads to the test reports being useless -- you cannot see whether a test is
actually successful or just not yet implemented. Calling $this->fail() in the unimplemented test
method does not help either, since then the test will be interpreted as a failure. This would be just as
wrong as interpreting an unimplemented test as a success.
To indicate that a certain test should be skipped we can use the method
$this->markTestSkipped( 'The MySQLi extension is not available.' );
This is useful when some precondition is not met. For instance a mySQLi extention is not available.
The descriptive messages passed as a parameter can be any string you like.
Training Course PHPUnit Nick Belhomme 2010 p. 27
TRAINING COURSE PHPUNIT
Test Doubles
We are going to add Tiles to our Game_Grid. First we add tests for getting the Tile.
public function testGetTileWhenNoneIsSet()
{
$grid = new Game_Grid(1,1);
$this->assertNull($grid->getTile(0,0));
}
public function testGetTileOutsideGrid()
{
$this->setExpectedException('Exception');
$grid = new Game_Grid(1,1);
$this->assertNull($grid->getTile(3,0));
}
When we have implemented our Game_Grid class to make the tests OK we can proceed to add
Tiles. How can we test this when the Game_Tile class hasn't been written yet? For this reason
amongst others we have Mocks at our disposal. They are very powerful.
public function testAddTile()
{
$tile = $this->getMock('stdClass', null, array(), 'Game_Tile');
$grid = new Game_Grid(1,1);
$this->assertType('Game_Grid', $grid->addTile($tile, 0, 0));
$this->assertType('Game_Tile', $grid->getTile(0,0));
}
At the moment there is no public api yet implemented for Game_Tile so Game_Grid cannot be
aware of it and thus not use it.
Mocking from stdClass will not work if there is interaction with Game_Tile from within the method
Game_Grid::addTile(). When such an implementation is needed we will need to create the
Game_Tile class and implement the called methods, properties or whatever is needed. Then we can
use the Game_Tile class as a blueprint like we did with stdClass. Making sure no real actions are
performed when called upon.
“Sometimes it is just plain hard to test the system under test (SUT) because it depends on other
components that cannot be used in the test environment. This could be because they aren't
available, they will not return the results needed for the test or because executing them would have
undesirable side effects. In other cases, our test strategy requires us to have more control or
visibility of the internal behavior of the SUT.
When we are writing a test in which we cannot (or chose not to) use a real depended-on component
(DOC), we can replace it with a Test Double. The Test Double doesn't have to behave exactly like
the real DOC; it merely has to provide the same API as the real one so that the SUT thinks it is the
real one! [Gerard Meszaros - Meszaros2007] “
Training Course PHPUnit Nick Belhomme 2010 p. 28
TRAINING COURSE PHPUNIT
$this->getMock() method of PHPUnit accepts several parameters.
protected function getMock($originalClassName, $methods = array(), array
$arguments = array(), $mockClassName = '', $callOriginalConstructor = TRUE,
$callOriginalClone = TRUE, $callAutoload = TRUE)
As you can see only the first parameter is mandatory all others are optional.
The first parameter is the name of an existing class needed to be mocked. PHPUnit will replace the
implementation from all the methods from this class with “return null;” unless otherwise stated
in parameter two or by further configuration of the mock object returned. The only method that will
remain unchanged by default is the constructor which can also be mocked by setting the fifth
parameter to false.
As we said parameter two gives you the possibility to specify only those methods for which you
want PHPUnit to change the implementation. Only the methods whose names are in the array are
replaced with a configurable test double. The behavior of the other methods is not changed.
The third parameter may hold a parameter array that is passed to the original class' constructor.
The fourth parameter can be used to specify a class name for the generated test double class.
The fifth parameter can be used to disable the call to the original class' constructor.
The sixth parameter can be used to disable the call to the original class' clone constructor
The seventh parameter can be used to disable __autoload() during the generation of the test
double class.
After implementing the tile functionality into the grid, we also need to implement the actions that
can be set. We want to be able to move from tile to tile on the grid. This means we have to be able
to add and get actions from Game_Grid. The tricky part in this implementation is that Game_Action
should be aware on which Game_Item, Game_Tile and or Game_Grid object it is acting it's magic
on. This was defined in the project requirements. This implies that when adding an action to the grid
the grid sets itself as a subject to the action. (see class diagram)
Stubbing is the practice of replacing an object with a test double that (optionally) returns configured
return values. You can use a stub to "replace a real component on which the SUT depends
so that the test has a control point for the indirect inputs of the SUT. This allows the test to force the
SUT down paths it might not otherwise execute".
Our implementation of Game_Action will be an abstract one. We could make it a fully implemented
class which accepts a name by a setter. But we do not want that, because we feel like every action in
the game should have a real hardcoded implementation and not generated on the fly. Of course this
can change in time. In example future implementations may come from a CMS tool and then we
will need the setters. But for now we do not want a mix of real objects with objects generated on the
Training Course PHPUnit Nick Belhomme 2010 p. 29
TRAINING COURSE PHPUNIT
fly.
First let us build the tests for Game_Action.
To test Game_Action we will use the PHPUnit method
PHPUnit_Testframework_TestCase::getMockForAbstractClass().
protected function getMockForAbstractClass($originalClassName, array $arguments
= array(), $mockClassName = '', $callOriginalConstructor = TRUE,
$callOriginalClone = TRUE, $callAutoload = TRUE)
Which will instantiate an abstract class with a test double. Making sure our abstract execute is
implemented.
Tests/PHPUnit/Library/Game/ActionTest.php
<?php
class Game_ActionTest extends PHPUnit_Framework_TestCase
{
public function setUp()
{
$this->_action = $this->getMockForAbstractClass('Game_Action');
}
public function testConstructorWithParams()
{
$grid = $this->getMock('Game_Grid', null, array(1,1));
$inventory = $this->getMock('Game_Inventory', null, array());
$this->assertType('Game_Action',
$this->getMockForAbstractClass('Game_Action', array($grid, $inventory)));
$this->assertType('Game_Action',
$this->getMockForAbstractClass('Game_Action', array($grid)));
$this->assertType('Game_Action',
$this->getMockForAbstractClass('Game_Action', array(null, $inventory)));
$this->assertType('Game_Action',
$this->getMockForAbstractClass('Game_Action', array(null, null)));
}
public function testConstructorWithParamsNotGrid()
{
$this->setExpectedException('PHPUnit_Framework_Error');
$this->assertType('Game_Action',
$this->getMockForAbstractClass('Game_Action', array('blabla')));
}
public function testConstructorWithParamsNotInventory()
{
$this->setExpectedException('PHPUnit_Framework_Error');
$this->assertType('Game_Action',
$this->getMockForAbstractClass('Game_Action', array(null, 'blabla')));
}
Training Course PHPUnit Nick Belhomme 2010 p. 30
TRAINING COURSE PHPUNIT
public function testSetItem()
{
require_once 'Item/Stub.php';
$item = new Game_Item_Stub();
$this->assertType('Game_Action', $this->_action->setSubject($item));
$tile = $this->getMock('Game_Tile');
$this->assertType('Game_Action', $this->_action->setSubject($tile));
$grid = $this->getMock('Game_Grid', null, array(1,1));
$this->assertType('Game_Action', $this->_action->setSubject($grid));
}
public function testSetItemIncorrectParam()
{
$this->setExpectedException('Exception');
$this->assertType('Game_Action', $this->_action->setSubject('blabla'));
}
public function testGetName()
{
$this->assertEquals('action', $this->_action->getName());
}
public function testGetSynonyms()
{
$this->assertType('array', $this->_action->getSynonyms());
}
public function testSetGrid()
{
$grid = $this->getMock('Game_Grid', null, array(1,1));
$this->assertType('Game_Action', $this->_action->setGrid($grid));
}
public function testSetGridWithFaultyParam()
{
$this->setExpectedException('PHPUnit_Framework_Error');
$this->_action->setGrid('wrong param');
}
public function testSetPersonalInventory()
{
$inventory = $this->getMock('Game_Inventory');
$this->assertType('Game_Action',
$this->_action->setPersonalInventory($inventory));
}
public function testSetPersonalInventoryWithFaultyParam()
{
$this->setExpectedException('PHPUnit_Framework_Error');
$this->_action->setPersonalInventory('wrong param');
}
}
Training Course PHPUnit Nick Belhomme 2010 p. 31
TRAINING COURSE PHPUNIT
Library/Game/Action.php
<?php
abstract class Game_Action
{
protected $_subject;
protected $_name = 'action';
protected $_synonyms = array();
protected $_grid;
protected $_personalInventory;
public function __construct(Game_Grid $grid = null,
Game_Inventory $inventory = null)
{
$this->_grid = $grid;
$this->_personalInventory = $inventory;
$this->_init();
}
public function setSubject($subject)
{
if (!($subject instanceOf Game_Item) && !($subject instanceOf Game_Tile)
&& !($subject instanceOf Game_Grid)) {
throw new Exception('Subject passed should be of type Game_subject,
Game_Tile, Game_Grid');
}
$this->_subject = $subject;
return $this;
}
public function getName()
{
return $this->_name;
}
public function getSynonyms()
{
return $this->_synonyms;
}
public function setGrid(Game_Grid $grid)
{
$this->_grid = $grid;
return $this;
}
public function setPersonalInventory(Game_Inventory $inventory)
{
$this->_personalInventory = $inventory;
return $this;
}
abstract public function execute();
protected function _getExecutedMessageSuccess()
{
Training Course PHPUnit Nick Belhomme 2010 p. 32
TRAINING COURSE PHPUNIT
echo 'action executed';
}
protected function _getExecutedMessageFailed()
{
echo 'action failed';
}
protected function _executeSuccess()
{
// template method
}
protected function _executeFailed()
{
// template method
}
protected function _init()
{
// template method
}
}
Now that Game_Action is implemented and tested it is time to create a helper stub. Because all the
objects depending on actions need an action name to differentiate between the different actions by
use of array keys, we will create a stub that will extend Game_Action and implement a setter.
Game_Grid is such an object. We want to give it four moving actions: GoNorth, goSouth, goWest,
goEast. To do this we must be able to change the name of the action.
The stub Game_Action_Stub will extend the Game_Action abstract class, set the default name and
add a setter.
Tests/PHPUnit/Library/Game/Action/Stub.php
<?php
class Game_Action_Stub extends Game_Action
{
public function execute()
{
return null;
}
// helper function for testing
public function setName($name)
{
$this->_name = $name;
}
}
Training Course PHPUnit Nick Belhomme 2010 p. 33
TRAINING COURSE PHPUNIT
After having Game_Action in place we can continue testing Game_Grid.
public function testAddActionAndGetActions()
{
require_once './Action/Stub.php';
$action = $this->getMock('Game_Action_Stub');
$action->expects($this->once())
->method('setGrid')
->with($this->isInstanceOf('Game_Grid'));
$action->expects($this->once())
->method('getName')
->will($this->returnValue('stubAction'));
$grid = new Game_Grid(2,2);
$actions = $grid->getActions();
$initialActionsCount = count($actions);
$this->assertType('Game_Grid', $grid->addAction($action));
$actions = $grid->getActions();
$this->assertEquals($initialActionsCount+1, count($actions));
// Same name, it will overwrite the previous one set
$action2 = $this->getMock('Game_Action');
$action2->expects($this->once())
->method('getName')
->will($this->returnValue('stubAction'));
$this->assertType('Game_Grid', $grid->addAction($action2));
$actions = $grid->getActions();
$this->assertEquals($initialActionsCount+1, count($actions));
}
We define in the test:
1) there should be a method Game_Grid::addAction and it should make one call
to the method setGrid on the passed Game_Action instance passing the
Game_Grid as a param.
2) Secondly it also should make a call to Game_Action::getName() and this
method will return the string 'stubAction'. Thus forcing Game_Grid to
accept a string value. Which it will actually use as the key in the
$actions array.
3) It also should have a fluent interface so the return value of addAction
should be Game_Grid itself.
4) Adding a new action will resolve in one action registered. Adding a second
with the same name will overwrite the previous one added. This requirement
is tested by creating another action with the same name and adding it to
the Grid.
public function addAction(Game_Action $action)
{
$this->_actions[$action->getName()] = $action;
$action->setGrid($this);
return $this;
}
Training Course PHPUnit Nick Belhomme 2010 p. 34
TRAINING COURSE PHPUNIT
Training Course PHPUnit Nick Belhomme 2010 p. 35
You cannot test the call to Game_Action::setGrid if you use
getMockForAbstractClass(). This method replaces the abstract methods by a
default implementation, leaving the others unchanged. It will always return
failure because it cannot register the call. Thus we use getMock() which needs
a non abstract class. Luckily for us we already defined the helper stub class
earlier.
TRAINING COURSE PHPUNIT
Dependencies
Tests/PHPUnit/Library/Game/GridTest.php
<?php
class Game_GridTest extends PHPUnit_Framework_TestCase
{
public function testConstructorNormalParamsWithGetGridSize()
{
$x = rand(1,100);
$y = rand(1,100);
$grid = new Game_Grid($x,$y);
$gridSize = $grid->getGridSize();
$this->assertEquals($x, $gridSize['x']);
$this->assertEquals($y, $gridSize['y']);
$grid = new Game_Grid((string) $x, (string) $y);
$gridSize = $grid->getGridSize();
$this->assertEquals($x, $gridSize['x']);
$this->assertEquals($y, $gridSize['y']);
}
public function constructorProviderBadParams()
{
return array(
array(0, 0),
array(0, 1),
array(1, 0),
array(-1, 1),
array(1, -1),
array(-1, -1),
);
}
/**
*
* @dataProvider constructorProviderBadParams
*/
public function testConstructNegativeOrZeroParam($paramX, $paramY)
{
$this->setExpectedException('Exception');
$grid = new Game_Grid($paramX, $paramY);
}
public function testGetTileWhenNoneIsSet()
{
$grid = new Game_Grid(1,1);
$this->assertNull($grid->getTile(0,0));
}
public function testGetTileFromPostionWhenNoTileIsSet()
{
$grid = new Game_Grid(1,1);
$this->assertNull($grid->getTileFromPosition());
}
public function testAddTile()
Training Course PHPUnit Nick Belhomme 2010 p. 36
TRAINING COURSE PHPUNIT
{
$tile = $this->getMock('Game_Tile', null, array(), 'Game_Tile_Mock',
false);
$grid = new Game_Grid(1,1);
$this->assertType('Game_Grid', $grid->addTile($tile, 0, 0));
$this->assertType('Game_Tile_Mock', $grid->getTile(0,0));
}
public function testAddTileOutsideGrid()
{
$this->setExpectedException('Exception');
$tile = $this->getMock('Game_Tile', null, array(), 'Game_Tile_Mock',
false);
$grid = new Game_Grid(1,1);
$grid->addTile($tile, 2, 3);
}
public function testIsOnGrid()
{
$grid = new Game_Grid(1,1);
$this->assertTrue($grid->isOnGrid(0,0));
$this->assertFalse($grid->isOnGrid(0,1));
}
public function testPosition()
{
$x = rand(1,100);
$y = rand(1,100);
$grid = new Game_Grid($x,$y);
$position = $grid->getPosition();
$this->assertEquals(0, $position['x']);
$this->assertEquals(0, $position['y']);
$newPositionX = rand(0,$x-1);
$newPositionY = rand(0,$y-1);
$grid->setPosition($newPositionX, $newPositionY);
$position = $grid->getPosition();
$this->assertEquals($newPositionX, $position['x']);
$this->assertEquals($newPositionY, $position['y']);
}
public function testSetPositionOutsideGrid()
{
$this->setExpectedException('Exception');
$grid = new Game_Grid(1,1);
$grid->setPosition(3,2);
}
public function testDefaultGridActions()
{
$grid = new Game_Grid(2,2);
$actions = $grid->getActions();
$this->assertEquals(4, count($actions));
$gameActionGoNorthFound = false;
$gameActionGoEastFound = false;
$gameActionGoSouthFound = false;
Training Course PHPUnit Nick Belhomme 2010 p. 37
TRAINING COURSE PHPUNIT
$gameActionGoWestFound = false;
foreach ($actions as $action) {
if ($action instanceof Game_Action_Go_North) {
$gameActionGoNorthFound = true;
}
if ($action instanceof Game_Action_Go_East) {
$gameActionGoEastFound = true;
}
if ($action instanceof Game_Action_Go_South) {
$gameActionGoSouthFound = true;
}
if ($action instanceof Game_Action_Go_West) {
$gameActionGoWestFound = true;
}
}
$this->assertTrue($gameActionGoNorthFound);
$this->assertTrue($gameActionGoEastFound);
$this->assertTrue($gameActionGoSouthFound);
$this->assertTrue($gameActionGoWestFound);
return $grid;
}
/**
* this dependency has been added to show this functionality
* @depends testDefaultGridActions
*/
public function testAddActionAndGetActions(Game_Grid $grid)
{
require_once './Action/Stub.php';
$action = $this->getMock('Game_Action_Stub');
$action->expects($this->once())
->method('setGrid')
->with($this->isInstanceOf('Game_Grid'));
$action->expects($this->once())
->method('getName')
->will($this->returnValue('stubAction'));
$actions = $grid->getActions();
$this->assertEquals(4, count($actions));
$this->assertType('Game_Grid', $grid->addAction($action));
$actions = $grid->getActions();
$this->assertEquals(5, count($actions));
// Same name, it will overwrite the previous one set
$action2 = $this->getMock('Game_Action');
$action2->expects($this->once())
->method('getName')
->will($this->returnValue('stubAction'));
$this->assertType('Game_Grid', $grid->addAction($action2));
$actions = $grid->getActions();
$this->assertEquals(5, count($actions));
}
}
Normally every test should be independent from each other. And should be able to be executed in
random order. Yet sometimes to quickly localize defects, we want our attention to be focused on
Training Course PHPUnit Nick Belhomme 2010 p. 38
TRAINING COURSE PHPUNIT
relevant failing tests. This is why PHPUnit skips the execution of a test when a depended-upon test
has failed. This improves defect localization by exploiting the dependencies between tests as shown
in the above testcase, “Exploiting the dependencies between tests”.
Purely functioning as an example we have changed some tests in the above code.
The test testAddActionAndGetActions depends on testDefaultGridActions.
Because if the defaultGridActions are not set correctly then our assertEquals for adding and getting
will also fail for testAddActionAndGetActions. We can define such a dependency by using a doc
block annotation @depends [test on which the test is dependant].
We have the possibility to pass fixtures as params in such cases, making sure that the test
environment is correctly set.
For the above dependency, testDefaultGridActions is called the producer and
testAddActionAndGetActions is called the consumer.
The DataProvider
Sometimes we want to test the same method with different params which should all result in the an
outcome predicted by the test. To facilitate this a dataProvider has been made available.
In the code in the previous chapter we have rewritten the tests from the chapter: “Testing for
Exceptions and PHP Errors” to use the dataProvider annotation. This will make our testing much
more easy to understand and extend. Because it removes redundancy.
For each array that is part of the dataProvider collection the test method will be called with the
contents of the array as its arguments.
You create a public method which returns an array or an object which implements the Iterator
interface. This method is called the dataProvider and you link it to the testmethod with a doc block
@dataProvider [provider method]
Training Course PHPUnit Nick Belhomme 2010 p. 39
A producer is a test method that yields its unit under test as return value.
A consumer is a test method that depends on one or more producers and their
return values.
PHPUnit does not change the order in which tests are executed, you have to
ensure that the dependencies of a test can actually be met before the test is run.
TRAINING COURSE PHPUNIT
Training Course PHPUnit Nick Belhomme 2010 p. 40
When a test receives input from both a @dataProvider method and from one or
more tests it @depends on, the arguments from the data provider will come
before the ones from depended-upon tests.
TRAINING COURSE PHPUNIT
setUp, tearDown and other template methods
One of the most time-consuming parts of writing tests is writing the code to setup the world to a
known state and then return it to its original state when the test is complete. This known state is
called the fixture of the test.
Setting and destructing a fixture for every test can be time consuming or a lot of copy and pasting,
the latter being against good coding practice. DRY (Do Not Repeat yourself) is something every
programmer should try to adhere to.
PHPUnit has made it easy for us to to provide us with hooks in the form of template methods.
Template methods are empty methods which are called in a predefined algorithm set by the class
which owns the template methods. You may give the algorithm a specific implementation by
implementing the methods.
Template methods available in order of execution.
• setUpBeforeClass():
Each time when your testcase class is run this method will be called once.
Here you can create for example a file with content that will not change in the life cycle of
the tests. And there for is not needed to be created every time between tests.
• setUp():
Before a test method is run, a template method called setUp() is invoked. setUp() is where
you create the objects against which you will test.
• assertPreConditions():
After each setup but before each test, this method is called. Here you can implement a check
on certain aspects of your testing stage.
[THE ACTUAL TEST WILL BE EXECUTED HERE]
• assertPostConditions():
After each test executed, this method is called. Here you can implement a check on certain
aspects of your testing stage.
• tearDown():
Once the test method has finished running, whether it succeeded or failed, another template
method called tearDown() is invoked. tearDown() is where you clean allocated external
resources like files or sockets. Also clean up objects for garbage collection.
• tearDownAfterClass():
Each time when your testcase class has finished running all tests and template methods this
last method will be called. This method will be called once. Here you can delete for example
the file that was set in the setupBeforeClass().
Training Course PHPUnit Nick Belhomme 2010 p. 41
TRAINING COURSE PHPUNIT
Testing your tests, code coverage
After creating a test for some functionality you should test the testcase for code coverage. What use
do tests have if they do not cover all of your application code. They will provide you with a false
sense of safety. Thus it is very important to tests your tests.
Luckily for us, PHPUnit has such a feature embedded into it's service. It uses the Xdebug extension.
PHPUnit can output code coverage in the following formats:
• html: coverage report in HTML format
phpunit --coverage-html <dir>
• clover: code coverage data in Clover XML format
phpunit --coverage-clover <file>
• source: code coverage / source data in XML format
phpunit --coverage-source <dir>
To produce a code coverage test for ActionTest use the following command.
phpunit --coverage-html /tmp/PHPUnit/Coverage/ ActionTest
We have already defined the setup for the code coverage in our configuration file from chapter
“Configure PHPUnit with a phpunit.xml configuration file”
<logging>
<log type="coverage-html" target="/tmp/PHPUnit/Coverage/"
charset="UTF-8" yui="true", highlight="false" lowUpperBound="35"
highLowerBound="70" />
</logging>
The <logging> element and its <log> children can be used to configure the logging of the test
execution.
type: specifies the type of logging
target: the target directory where the log should be written to
charset: the character encoding for the log
yui: Yahoo User Interface, true enables javascript on click events.
Highlight: activates the code syntax highlighting.
lowUpperBound: overwrite the default percentage of 35 to your upper limit of max coverage
percentage that is needed to qualiy as low.
HighLowerBound: overwrite the default percentage of 70 to your minimum coverage percentage is
needed to qualify as high coverage.
Training Course PHPUnit Nick Belhomme 2010 p. 42
TRAINING COURSE PHPUNIT
Increase the readability by creating smaller tests and adding
assertion messages.
Want to know more?
Want to learn about all the nitty gritty hidden features
of PHPUnit?
Want to integrate PHPUnit in your IDE?
Want to integrate PHPUnit in your ZF projects?
Want to fully automate the running of tests with continuous
integration?
GOOD NEWS, YOU CAN!
• I give workshops at community events for free (technically
they are sponsored)
• I can give a workshop at the company for which you work.
(Your boss pays and during office hours you get to become a
PHPUnit expert, pretty neat I would say)
• Purchase my upcoming book on an important framework.
THANK YOU FOR READING,
Nick Belhomme
follow me on twitter: @NickBelhomme
http://nickbelhomme.com/phpunit-training
Training Course PHPUnit Nick Belhomme 2010 p. 43

More Related Content

What's hot

Prezentacja "Praca z Gitem" - Dawid Cieszyński OLMUG 22.01.14
Prezentacja "Praca z Gitem" - Dawid Cieszyński OLMUG 22.01.14Prezentacja "Praca z Gitem" - Dawid Cieszyński OLMUG 22.01.14
Prezentacja "Praca z Gitem" - Dawid Cieszyński OLMUG 22.01.14Sagittario
 
Introduzione a Git (ITA - 2017)
Introduzione a Git (ITA - 2017)Introduzione a Git (ITA - 2017)
Introduzione a Git (ITA - 2017)Valerio Radice
 
導讀持續交付 2.0 - 談當代軟體交付之虛實融合
導讀持續交付 2.0 - 談當代軟體交付之虛實融合導讀持續交付 2.0 - 談當代軟體交付之虛實融合
導讀持續交付 2.0 - 談當代軟體交付之虛實融合Rick Hwang
 
QA your code: The new Unity Test Framework – Unite Copenhagen 2019
QA your code: The new Unity Test Framework – Unite Copenhagen 2019QA your code: The new Unity Test Framework – Unite Copenhagen 2019
QA your code: The new Unity Test Framework – Unite Copenhagen 2019Unity Technologies
 
Multibranch pipelineでいろいろ学んだこと
Multibranch pipelineでいろいろ学んだことMultibranch pipelineでいろいろ学んだこと
Multibranch pipelineでいろいろ学んだことRecruit Lifestyle Co., Ltd.
 
Tecnicas Para Planejamento E Execucao De Testes De Software
Tecnicas Para Planejamento E Execucao De Testes De SoftwareTecnicas Para Planejamento E Execucao De Testes De Software
Tecnicas Para Planejamento E Execucao De Testes De Softwaremarthahuback
 
TypeScript 開發實戰:開發即時互動的 html5 websocket 聊天室應用程式
TypeScript 開發實戰:開發即時互動的 html5 websocket 聊天室應用程式TypeScript 開發實戰:開發即時互動的 html5 websocket 聊天室應用程式
TypeScript 開發實戰:開發即時互動的 html5 websocket 聊天室應用程式Will Huang
 
Modelo V - Desenvolvimento de Software
Modelo V - Desenvolvimento de SoftwareModelo V - Desenvolvimento de Software
Modelo V - Desenvolvimento de SoftwareBruno Bitencourt Luiz
 
JavaのテストGroovyでいいのではないかという話
JavaのテストGroovyでいいのではないかという話JavaのテストGroovyでいいのではないかという話
JavaのテストGroovyでいいのではないかという話disc99_
 
JavaScriptCore.framework の普通な使い方 #cocoa_kansai
JavaScriptCore.framework の普通な使い方 #cocoa_kansaiJavaScriptCore.framework の普通な使い方 #cocoa_kansai
JavaScriptCore.framework の普通な使い方 #cocoa_kansaiTomohiro Kumagai
 
[JSUG SpringOne 2021 報告会]見えてきたSpring 6.0の方向性
[JSUG SpringOne 2021 報告会]見えてきたSpring 6.0の方向性[JSUG SpringOne 2021 報告会]見えてきたSpring 6.0の方向性
[JSUG SpringOne 2021 報告会]見えてきたSpring 6.0の方向性ikeyat
 
運用構築技術者の為のPSプログラミング第1回
運用構築技術者の為のPSプログラミング第1回運用構築技術者の為のPSプログラミング第1回
運用構築技術者の為のPSプログラミング第1回Shigeharu Yamaoka
 
継続的インテグレーションとテストの話
継続的インテグレーションとテストの話継続的インテグレーションとテストの話
継続的インテグレーションとテストの話Preferred Networks
 
テスト駆動開発へようこそ
テスト駆動開発へようこそテスト駆動開発へようこそ
テスト駆動開発へようこそShuji Watanabe
 
節子、それViewControllerやない...、FatViewControllerや...。
節子、それViewControllerやない...、FatViewControllerや...。節子、それViewControllerやない...、FatViewControllerや...。
節子、それViewControllerやない...、FatViewControllerや...。Kenji Tanaka
 
Unit Testing Using Mockito in Android (1).pdf
Unit Testing Using Mockito in Android (1).pdfUnit Testing Using Mockito in Android (1).pdf
Unit Testing Using Mockito in Android (1).pdfKaty Slemon
 
Swift で JavaScript 始めませんか? #iOSDC
Swift で JavaScript 始めませんか? #iOSDCSwift で JavaScript 始めませんか? #iOSDC
Swift で JavaScript 始めませんか? #iOSDCTomohiro Kumagai
 

What's hot (20)

Prezentacja "Praca z Gitem" - Dawid Cieszyński OLMUG 22.01.14
Prezentacja "Praca z Gitem" - Dawid Cieszyński OLMUG 22.01.14Prezentacja "Praca z Gitem" - Dawid Cieszyński OLMUG 22.01.14
Prezentacja "Praca z Gitem" - Dawid Cieszyński OLMUG 22.01.14
 
Introduzione a Git (ITA - 2017)
Introduzione a Git (ITA - 2017)Introduzione a Git (ITA - 2017)
Introduzione a Git (ITA - 2017)
 
Controllo di versione e Git
Controllo di versione e GitControllo di versione e Git
Controllo di versione e Git
 
導讀持續交付 2.0 - 談當代軟體交付之虛實融合
導讀持續交付 2.0 - 談當代軟體交付之虛實融合導讀持續交付 2.0 - 談當代軟體交付之虛實融合
導讀持續交付 2.0 - 談當代軟體交付之虛實融合
 
QA your code: The new Unity Test Framework – Unite Copenhagen 2019
QA your code: The new Unity Test Framework – Unite Copenhagen 2019QA your code: The new Unity Test Framework – Unite Copenhagen 2019
QA your code: The new Unity Test Framework – Unite Copenhagen 2019
 
Multibranch pipelineでいろいろ学んだこと
Multibranch pipelineでいろいろ学んだことMultibranch pipelineでいろいろ学んだこと
Multibranch pipelineでいろいろ学んだこと
 
Maven
MavenMaven
Maven
 
Tecnicas Para Planejamento E Execucao De Testes De Software
Tecnicas Para Planejamento E Execucao De Testes De SoftwareTecnicas Para Planejamento E Execucao De Testes De Software
Tecnicas Para Planejamento E Execucao De Testes De Software
 
TypeScript 開發實戰:開發即時互動的 html5 websocket 聊天室應用程式
TypeScript 開發實戰:開發即時互動的 html5 websocket 聊天室應用程式TypeScript 開發實戰:開發即時互動的 html5 websocket 聊天室應用程式
TypeScript 開發實戰:開發即時互動的 html5 websocket 聊天室應用程式
 
Modelo V - Desenvolvimento de Software
Modelo V - Desenvolvimento de SoftwareModelo V - Desenvolvimento de Software
Modelo V - Desenvolvimento de Software
 
JavaのテストGroovyでいいのではないかという話
JavaのテストGroovyでいいのではないかという話JavaのテストGroovyでいいのではないかという話
JavaのテストGroovyでいいのではないかという話
 
JavaScriptCore.framework の普通な使い方 #cocoa_kansai
JavaScriptCore.framework の普通な使い方 #cocoa_kansaiJavaScriptCore.framework の普通な使い方 #cocoa_kansai
JavaScriptCore.framework の普通な使い方 #cocoa_kansai
 
[JSUG SpringOne 2021 報告会]見えてきたSpring 6.0の方向性
[JSUG SpringOne 2021 報告会]見えてきたSpring 6.0の方向性[JSUG SpringOne 2021 報告会]見えてきたSpring 6.0の方向性
[JSUG SpringOne 2021 報告会]見えてきたSpring 6.0の方向性
 
運用構築技術者の為のPSプログラミング第1回
運用構築技術者の為のPSプログラミング第1回運用構築技術者の為のPSプログラミング第1回
運用構築技術者の為のPSプログラミング第1回
 
Webrender 1.0
Webrender 1.0Webrender 1.0
Webrender 1.0
 
継続的インテグレーションとテストの話
継続的インテグレーションとテストの話継続的インテグレーションとテストの話
継続的インテグレーションとテストの話
 
テスト駆動開発へようこそ
テスト駆動開発へようこそテスト駆動開発へようこそ
テスト駆動開発へようこそ
 
節子、それViewControllerやない...、FatViewControllerや...。
節子、それViewControllerやない...、FatViewControllerや...。節子、それViewControllerやない...、FatViewControllerや...。
節子、それViewControllerやない...、FatViewControllerや...。
 
Unit Testing Using Mockito in Android (1).pdf
Unit Testing Using Mockito in Android (1).pdfUnit Testing Using Mockito in Android (1).pdf
Unit Testing Using Mockito in Android (1).pdf
 
Swift で JavaScript 始めませんか? #iOSDC
Swift で JavaScript 始めませんか? #iOSDCSwift で JavaScript 始めませんか? #iOSDC
Swift で JavaScript 始めませんか? #iOSDC
 

Similar to Cursus phpunit

PHPUnit with Magento
PHPUnit with MagentoPHPUnit with Magento
PHPUnit with MagentoTu Hoang
 
Test Driven Development (TDD) with FlexUnit 4 - 360|Flex San Jose preso
Test Driven Development (TDD) with FlexUnit 4 - 360|Flex San Jose presoTest Driven Development (TDD) with FlexUnit 4 - 360|Flex San Jose preso
Test Driven Development (TDD) with FlexUnit 4 - 360|Flex San Jose presoElad Elrom
 
Test Driven Development (TDD) Preso 360|Flex 2010
Test Driven Development (TDD) Preso 360|Flex 2010Test Driven Development (TDD) Preso 360|Flex 2010
Test Driven Development (TDD) Preso 360|Flex 2010guest5639fa9
 
Unit Tests? It is Very Simple and Easy!
Unit Tests? It is Very Simple and Easy!Unit Tests? It is Very Simple and Easy!
Unit Tests? It is Very Simple and Easy!Return on Intelligence
 
Unit testing php-unit - phing - selenium_v2
Unit testing   php-unit - phing - selenium_v2Unit testing   php-unit - phing - selenium_v2
Unit testing php-unit - phing - selenium_v2Tricode (part of Dept)
 
Put an end to regression with codeception testing
Put an end to regression with codeception testingPut an end to regression with codeception testing
Put an end to regression with codeception testingJoe Ferguson
 
Test Driven Development
Test Driven DevelopmentTest Driven Development
Test Driven Developmentbhochhi
 
Testing practicies not only in scala
Testing practicies not only in scalaTesting practicies not only in scala
Testing practicies not only in scalaPaweł Panasewicz
 
PHPUnit: from zero to hero
PHPUnit: from zero to heroPHPUnit: from zero to hero
PHPUnit: from zero to heroJeremy Cook
 
Agile Values, Principles and Practices
Agile Values, Principles and PracticesAgile Values, Principles and Practices
Agile Values, Principles and Practicesjackcrews
 
Test-Driven Development In Action
Test-Driven Development In ActionTest-Driven Development In Action
Test-Driven Development In ActionJon Kruger
 
Test Driven Development - Overview and Adoption
Test Driven Development - Overview and AdoptionTest Driven Development - Overview and Adoption
Test Driven Development - Overview and AdoptionPyxis Technologies
 
assertYourself - Breaking the Theories and Assumptions of Unit Testing in Flex
assertYourself - Breaking the Theories and Assumptions of Unit Testing in FlexassertYourself - Breaking the Theories and Assumptions of Unit Testing in Flex
assertYourself - Breaking the Theories and Assumptions of Unit Testing in Flexmichael.labriola
 
Developers’ mDay u Banjoj Luci - Milan Popović, PHP Srbija – Testimony (about...
Developers’ mDay u Banjoj Luci - Milan Popović, PHP Srbija – Testimony (about...Developers’ mDay u Banjoj Luci - Milan Popović, PHP Srbija – Testimony (about...
Developers’ mDay u Banjoj Luci - Milan Popović, PHP Srbija – Testimony (about...mCloud
 

Similar to Cursus phpunit (20)

PHPUnit
PHPUnitPHPUnit
PHPUnit
 
TDD - Agile
TDD - Agile TDD - Agile
TDD - Agile
 
PHPUnit with Magento
PHPUnit with MagentoPHPUnit with Magento
PHPUnit with Magento
 
Test Driven Development (TDD) with FlexUnit 4 - 360|Flex San Jose preso
Test Driven Development (TDD) with FlexUnit 4 - 360|Flex San Jose presoTest Driven Development (TDD) with FlexUnit 4 - 360|Flex San Jose preso
Test Driven Development (TDD) with FlexUnit 4 - 360|Flex San Jose preso
 
Test Driven Development (TDD) Preso 360|Flex 2010
Test Driven Development (TDD) Preso 360|Flex 2010Test Driven Development (TDD) Preso 360|Flex 2010
Test Driven Development (TDD) Preso 360|Flex 2010
 
Python and test
Python and testPython and test
Python and test
 
Tdd
TddTdd
Tdd
 
Why test with flex unit
Why test with flex unitWhy test with flex unit
Why test with flex unit
 
TDD Workshop UTN 2012
TDD Workshop UTN 2012TDD Workshop UTN 2012
TDD Workshop UTN 2012
 
Unit Tests? It is Very Simple and Easy!
Unit Tests? It is Very Simple and Easy!Unit Tests? It is Very Simple and Easy!
Unit Tests? It is Very Simple and Easy!
 
Unit testing php-unit - phing - selenium_v2
Unit testing   php-unit - phing - selenium_v2Unit testing   php-unit - phing - selenium_v2
Unit testing php-unit - phing - selenium_v2
 
Put an end to regression with codeception testing
Put an end to regression with codeception testingPut an end to regression with codeception testing
Put an end to regression with codeception testing
 
Test Driven Development
Test Driven DevelopmentTest Driven Development
Test Driven Development
 
Testing practicies not only in scala
Testing practicies not only in scalaTesting practicies not only in scala
Testing practicies not only in scala
 
PHPUnit: from zero to hero
PHPUnit: from zero to heroPHPUnit: from zero to hero
PHPUnit: from zero to hero
 
Agile Values, Principles and Practices
Agile Values, Principles and PracticesAgile Values, Principles and Practices
Agile Values, Principles and Practices
 
Test-Driven Development In Action
Test-Driven Development In ActionTest-Driven Development In Action
Test-Driven Development In Action
 
Test Driven Development - Overview and Adoption
Test Driven Development - Overview and AdoptionTest Driven Development - Overview and Adoption
Test Driven Development - Overview and Adoption
 
assertYourself - Breaking the Theories and Assumptions of Unit Testing in Flex
assertYourself - Breaking the Theories and Assumptions of Unit Testing in FlexassertYourself - Breaking the Theories and Assumptions of Unit Testing in Flex
assertYourself - Breaking the Theories and Assumptions of Unit Testing in Flex
 
Developers’ mDay u Banjoj Luci - Milan Popović, PHP Srbija – Testimony (about...
Developers’ mDay u Banjoj Luci - Milan Popović, PHP Srbija – Testimony (about...Developers’ mDay u Banjoj Luci - Milan Popović, PHP Srbija – Testimony (about...
Developers’ mDay u Banjoj Luci - Milan Popović, PHP Srbija – Testimony (about...
 

More from Nick Belhomme

Vagrant move over, here is Docker
Vagrant move over, here is DockerVagrant move over, here is Docker
Vagrant move over, here is DockerNick Belhomme
 
Mastering selenium for automated acceptance tests
Mastering selenium for automated acceptance testsMastering selenium for automated acceptance tests
Mastering selenium for automated acceptance testsNick Belhomme
 
PHP traits, treat or threat?
PHP traits, treat or threat?PHP traits, treat or threat?
PHP traits, treat or threat?Nick Belhomme
 
PHP Quality Assurance Workshop PHPBenelux
PHP Quality Assurance Workshop PHPBeneluxPHP Quality Assurance Workshop PHPBenelux
PHP Quality Assurance Workshop PHPBeneluxNick Belhomme
 
Mastering Namespaces in PHP
Mastering Namespaces in PHPMastering Namespaces in PHP
Mastering Namespaces in PHPNick Belhomme
 
Zend Framework Form: Mastering Decorators
Zend Framework Form: Mastering DecoratorsZend Framework Form: Mastering Decorators
Zend Framework Form: Mastering DecoratorsNick Belhomme
 
Zend Framework 1.8 workshop
Zend Framework 1.8 workshopZend Framework 1.8 workshop
Zend Framework 1.8 workshopNick Belhomme
 

More from Nick Belhomme (7)

Vagrant move over, here is Docker
Vagrant move over, here is DockerVagrant move over, here is Docker
Vagrant move over, here is Docker
 
Mastering selenium for automated acceptance tests
Mastering selenium for automated acceptance testsMastering selenium for automated acceptance tests
Mastering selenium for automated acceptance tests
 
PHP traits, treat or threat?
PHP traits, treat or threat?PHP traits, treat or threat?
PHP traits, treat or threat?
 
PHP Quality Assurance Workshop PHPBenelux
PHP Quality Assurance Workshop PHPBeneluxPHP Quality Assurance Workshop PHPBenelux
PHP Quality Assurance Workshop PHPBenelux
 
Mastering Namespaces in PHP
Mastering Namespaces in PHPMastering Namespaces in PHP
Mastering Namespaces in PHP
 
Zend Framework Form: Mastering Decorators
Zend Framework Form: Mastering DecoratorsZend Framework Form: Mastering Decorators
Zend Framework Form: Mastering Decorators
 
Zend Framework 1.8 workshop
Zend Framework 1.8 workshopZend Framework 1.8 workshop
Zend Framework 1.8 workshop
 

Recently uploaded

Microsoft 365 Copilot: How to boost your productivity with AI – Part two: Dat...
Microsoft 365 Copilot: How to boost your productivity with AI – Part two: Dat...Microsoft 365 Copilot: How to boost your productivity with AI – Part two: Dat...
Microsoft 365 Copilot: How to boost your productivity with AI – Part two: Dat...Nikki Chapple
 
Manual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance AuditManual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance AuditSkynet Technologies
 
A Framework for Development in the AI Age
A Framework for Development in the AI AgeA Framework for Development in the AI Age
A Framework for Development in the AI AgeCprime
 
QMMS Lesson 2 - Using MS Excel Formula.pdf
QMMS Lesson 2 - Using MS Excel Formula.pdfQMMS Lesson 2 - Using MS Excel Formula.pdf
QMMS Lesson 2 - Using MS Excel Formula.pdfROWELL MARQUINA
 
Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024TopCSSGallery
 
Email Marketing Automation for Bonterra Impact Management (fka Social Solutio...
Email Marketing Automation for Bonterra Impact Management (fka Social Solutio...Email Marketing Automation for Bonterra Impact Management (fka Social Solutio...
Email Marketing Automation for Bonterra Impact Management (fka Social Solutio...Jeffrey Haguewood
 
Digital Tools & AI in Career Development
Digital Tools & AI in Career DevelopmentDigital Tools & AI in Career Development
Digital Tools & AI in Career DevelopmentMahmoud Rabie
 
Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...
Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...
Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...panagenda
 
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better StrongerModern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better Strongerpanagenda
 
Testing tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesTesting tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesKari Kakkonen
 
Kuma Meshes Part I - The basics - A tutorial
Kuma Meshes Part I - The basics - A tutorialKuma Meshes Part I - The basics - A tutorial
Kuma Meshes Part I - The basics - A tutorialJoão Esperancinha
 
React JS; all concepts. Contains React Features, JSX, functional & Class comp...
React JS; all concepts. Contains React Features, JSX, functional & Class comp...React JS; all concepts. Contains React Features, JSX, functional & Class comp...
React JS; all concepts. Contains React Features, JSX, functional & Class comp...Karmanjay Verma
 
Generative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfGenerative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfIngrid Airi González
 
Varsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
Varsha Sewlal- Cyber Attacks on Critical Critical InfrastructureVarsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
Varsha Sewlal- Cyber Attacks on Critical Critical Infrastructureitnewsafrica
 
Generative AI - Gitex v1Generative AI - Gitex v1.pptx
Generative AI - Gitex v1Generative AI - Gitex v1.pptxGenerative AI - Gitex v1Generative AI - Gitex v1.pptx
Generative AI - Gitex v1Generative AI - Gitex v1.pptxfnnc6jmgwh
 
All These Sophisticated Attacks, Can We Really Detect Them - PDF
All These Sophisticated Attacks, Can We Really Detect Them - PDFAll These Sophisticated Attacks, Can We Really Detect Them - PDF
All These Sophisticated Attacks, Can We Really Detect Them - PDFMichael Gough
 
Design pattern talk by Kaya Weers - 2024 (v2)
Design pattern talk by Kaya Weers - 2024 (v2)Design pattern talk by Kaya Weers - 2024 (v2)
Design pattern talk by Kaya Weers - 2024 (v2)Kaya Weers
 
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesAssure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesThousandEyes
 
JET Technology Labs White Paper for Virtualized Security and Encryption Techn...
JET Technology Labs White Paper for Virtualized Security and Encryption Techn...JET Technology Labs White Paper for Virtualized Security and Encryption Techn...
JET Technology Labs White Paper for Virtualized Security and Encryption Techn...amber724300
 

Recently uploaded (20)

Microsoft 365 Copilot: How to boost your productivity with AI – Part two: Dat...
Microsoft 365 Copilot: How to boost your productivity with AI – Part two: Dat...Microsoft 365 Copilot: How to boost your productivity with AI – Part two: Dat...
Microsoft 365 Copilot: How to boost your productivity with AI – Part two: Dat...
 
Manual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance AuditManual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance Audit
 
A Framework for Development in the AI Age
A Framework for Development in the AI AgeA Framework for Development in the AI Age
A Framework for Development in the AI Age
 
QMMS Lesson 2 - Using MS Excel Formula.pdf
QMMS Lesson 2 - Using MS Excel Formula.pdfQMMS Lesson 2 - Using MS Excel Formula.pdf
QMMS Lesson 2 - Using MS Excel Formula.pdf
 
Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024
 
Email Marketing Automation for Bonterra Impact Management (fka Social Solutio...
Email Marketing Automation for Bonterra Impact Management (fka Social Solutio...Email Marketing Automation for Bonterra Impact Management (fka Social Solutio...
Email Marketing Automation for Bonterra Impact Management (fka Social Solutio...
 
Digital Tools & AI in Career Development
Digital Tools & AI in Career DevelopmentDigital Tools & AI in Career Development
Digital Tools & AI in Career Development
 
Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...
Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...
Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...
 
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better StrongerModern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
 
Testing tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesTesting tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examples
 
Kuma Meshes Part I - The basics - A tutorial
Kuma Meshes Part I - The basics - A tutorialKuma Meshes Part I - The basics - A tutorial
Kuma Meshes Part I - The basics - A tutorial
 
React JS; all concepts. Contains React Features, JSX, functional & Class comp...
React JS; all concepts. Contains React Features, JSX, functional & Class comp...React JS; all concepts. Contains React Features, JSX, functional & Class comp...
React JS; all concepts. Contains React Features, JSX, functional & Class comp...
 
Generative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfGenerative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdf
 
Varsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
Varsha Sewlal- Cyber Attacks on Critical Critical InfrastructureVarsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
Varsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
 
Generative AI - Gitex v1Generative AI - Gitex v1.pptx
Generative AI - Gitex v1Generative AI - Gitex v1.pptxGenerative AI - Gitex v1Generative AI - Gitex v1.pptx
Generative AI - Gitex v1Generative AI - Gitex v1.pptx
 
All These Sophisticated Attacks, Can We Really Detect Them - PDF
All These Sophisticated Attacks, Can We Really Detect Them - PDFAll These Sophisticated Attacks, Can We Really Detect Them - PDF
All These Sophisticated Attacks, Can We Really Detect Them - PDF
 
Design pattern talk by Kaya Weers - 2024 (v2)
Design pattern talk by Kaya Weers - 2024 (v2)Design pattern talk by Kaya Weers - 2024 (v2)
Design pattern talk by Kaya Weers - 2024 (v2)
 
How Tech Giants Cut Corners to Harvest Data for A.I.
How Tech Giants Cut Corners to Harvest Data for A.I.How Tech Giants Cut Corners to Harvest Data for A.I.
How Tech Giants Cut Corners to Harvest Data for A.I.
 
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesAssure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
 
JET Technology Labs White Paper for Virtualized Security and Encryption Techn...
JET Technology Labs White Paper for Virtualized Security and Encryption Techn...JET Technology Labs White Paper for Virtualized Security and Encryption Techn...
JET Technology Labs White Paper for Virtualized Security and Encryption Techn...
 

Cursus phpunit

  • 1. TRAINING COURSE PHPUNIT Training Course PHPUnit Training Course PHPUnit Nick Belhomme 2010 p. 1
  • 2. TRAINING COURSE PHPUNIT Goal of this course We will introduce you into the art of unit testing. What is Unit testing and how does it work. Together we will build a testing suite for a domain model (computer text based game) and finally implement this model into Zend Framework 1.11 and up. Making you comfortable with the PHPUnit testing framework. Training Course PHPUnit Nick Belhomme 2010 p. 2
  • 3. TRAINING COURSE PHPUNIT What where how? Training Course PHPUnit Nick Belhomme 2010 p. 3
  • 4. TRAINING COURSE PHPUNIT What is Unit Testing? Unit Testing is the testing of units. The tests are done to ensure that each unit is working like it is intended to work. The tests should be automatic and performed on regular intervals performing validation and verification on the correct working of the units tested. Ideally each test should be independent of another. A unit to be tested is a small piece of software code. Today in Object oriented programming languages these small pieces of software code are generally individual methods from a specific class. Unit testing gives us a way to test our implementations, design and behavior on the classes we write. Today we can claim that Unit testing is a fundamental part of quality modern software development. Benefits of Unit Testing Unit testing has three main benefits: • It makes you think about your application and about the implementation route you have chosen / are about to chose instead of running off half cocked implementing bad design decisions. • It gives an immediate view on the proper functioning of the system and facilitates locating a defect in the system. • It facilitates the refactoring of existing code. Which is equally important to guarantee the longevity of an application. "Refactoring is the process of changing a software system in such a way that it does not alter the external behavior of the code yet improves its internal structure. It is a disciplined way to clean up code that minimizes the chances of introducing bugs. [Preface Refactoring Martin Fowler]" When something breaks the application because of external factors (ie. DB credential changes) or because changes made to the code, Unit Tests will quickly show you were the error or bug has been introduced. Allowing you to quickly locate and fix the bug. In the end speeding up development time. Because of the tests in place you as a developer will feel more confident in your code and in your future changes. Reflecting that confidence to customers, management and other developers. Training Course PHPUnit Nick Belhomme 2010 p. 4
  • 5. TRAINING COURSE PHPUNIT Testing pieces of software Mostly for (PHP) developers testing their code was programming something and displaying it in the browser. If it worked we moved on to the next piece of software to implement. In a modern development cycle unit testing is a core component. Instead of testing stuff in the browser we also test by automating different scenarios. “In general if you think you need a var_dump() for testing your code, you are better of making that test eternally embedded in your test-suite. Martin-Fowler” Every programmer makes mistakes. What differentiates the good from the bad is that the good programmer uses tests to detect his mistakes ASAP. Reducing debugging costs to a minimum and making the product more stable. Okay up to our first test. We are going to test the CAST operator <?php $fixture = new stdClass(); $fixture->id = 1248; $fixture->name = 'Robin Hood'; // $fixture is expected to be an object from the type stdClass // with two properties set $fixture = (array) $fixture; // $fixture is expected to be an array // with two key value pairs set If we want to check if the behavior of the (array) cast operator is working as intended we can add a simple check <?php $fixture = new stdClass(); $fixture->id = 1248; $fixture->name = 'Robin Hood'; echo is_object($fixture), PHP_EOL; echo $fixture->id == 1248, PHP_EOL; echo $fixture->name == 'Robin Hood', PHP_EOL; $fixture = (array) $fixture; echo is_array($fixture),PHP_EOL; echo $fixture['id'] == 1248, PHP_EOL; echo $fixture['name']== 'Robin Hood', PHP_EOL; Training Course PHPUnit Nick Belhomme 2010 p. 5
  • 6. TRAINING COURSE PHPUNIT Everything is working as expected because we get for the 6 tests all 1 But at this time the tests are not automated and still require a human to check if every test has returned 1. Scanning over a big list of “ones” could get tiring and frustrating if you have to do it several times a day. We should automate this so that the tests only report unwanted behavior. Let's improve this. <?php $fixture = new stdClass(); $fixture->id = 1248; $fixture->name = 'Robin Hood'; assertTrue(is_object($fixture)); assertTrue($fixture->id == 1248); assertTrue($fixture->name == 'Robin Hood'); $fixture = (array) $fixture; assertTrue(is_array($fixture)); assertTrue($fixture['id'] == 1248); assertTrue($fixture['name']== 'Robin Hood'); function assertTrue($condition) { if (!$condition) { throw new Exception('Assertion failed.'); } } The tests are now fully automated and only report when something fails. There are several test suites readily available to make life easier for you as a developer. These test suites have the functions like assertTrue already implemented amongst many others. This course is on the topic on the XUnit family of testing frameworks and more specifically PHPUnit. Training Course PHPUnit Nick Belhomme 2010 p. 6
  • 7. TRAINING COURSE PHPUNIT PHPUnit as a part of the XUnit Family The XUnit family is a collective of various code-driven testing frameworks. They provide an automated solution with no need to write the same tests many times, and no need to remember what the result should be of each test. The name XUnit is a derivation of JUnit the first to be widely known. Each programming language has his own XUnit framework(s). The two most famous Unit testing frameworks for PHP are SimpleTest by Marcus Baker and PHPUnit by Sebastian Bergmann. In this course we have chosen to elaborate PHPUnit. The reason is because it is more widely adopted for PHP. Which is reflected by the adoption for it in major PHP frameworks like Zend Framework and Symfony. Another reason is SimpleTest is not really maintained anymore. When to create Unit tests When you have classes which aren't covered yet by tests you should start to setup a testing suite to make sure your code gets validated and checked on a regular interval. If you put those tests in place you will become more confident in adding extra functionality and refactoring your existing code base. You could follow this methodology. Writing first the classes and then the tests. While this is better than writing no tests at all it has some drawbacks: • Some tests will not get written. • Not that much thought will be put in the initial implementation. And putting initial thought into your implementation is a key principal for every good programmer. If we analyse the previous thought cycle we come to the conclusion that it is a wise decision to put first the tests in place and then the implementation. This is exactly what Test Driven Development TDD is all about. It dictates that all tests should be written prior to implementing the code. TDD is a derivation from Xtreme Programming and it makes much sense to develop this way. When we develop an application we should think about it before implementing it. TDD forces this path. And puts tests in place before even implementing a single piece of application code. But you shouldn't think in terms of tests but in terms of behavior. This brings us to Behavior Driven Development. Which has the benefits of TDD but shifts the focus. “So if it's not about testing, what's it about? It's about figuring out what you are trying to do before you run off half-cocked to try to do it. You write a specification that nails down a small aspect of behaviour in a concise, unambiguous, and executable form. It's that simple. Does that mean you write tests? No. It means you write specifications of what your code will have to do. It means you specify the behaviour of your code ahead of time. But not far ahead of time. In fact, just before you write the code is best because that's when you have as much information at hand as you will up to that point. Like well done TDD, Training Course PHPUnit Nick Belhomme 2010 p. 7
  • 8. TRAINING COURSE PHPUNIT you work in tiny increments... specifying one small aspect of behaviour at a time, then implementing it. When you realize that it's all about specifying behaviour and not writing tests, your point of view shifts. Suddenly the idea of having a Test class for each of your production classes is ridiculously limiting. And the thought of testing each of your methods with its own test method (in a 1-1 relationship) will be laughable. —Dave Astels . Training Course PHPUnit Nick Belhomme 2010 p. 8
  • 9. TRAINING COURSE PHPUNIT Getting Started Training Course PHPUnit Nick Belhomme 2010 p. 9
  • 10. TRAINING COURSE PHPUNIT Installing PHPUnit Installing PHPUnit is straight forward. You use the Pear Installer, set up your path (which should already be done for PEAR) and that's it. 1. If you have PEAR already installed skip to step 2. go to your phpPEAR directory and do: WINDOWS: php phar.require_hash=0 go-pear.phar register the PEAR path to your system environment: right click "this computer" => "properties" => "advanced" => "environment variables". In the field "system variables" select PATH, "edit" and add the full path to PEAR to the end of the list. *NIX: php go-pear.phar register the PEAR path to your system environment 2. update your local PEAR environment: pear channel-discover pear.phpunit.de 3. install PHPUnit pear install phpunit/PHPUnit Defining the project We are going to build a text based adventure game. The setup of the game will be much alike the old adventure games of Sierra and Lucas Arts but then purely text based. We will have a character (you) which can travel from location to location, picking up objects, manipulating the objects and talking to characters. So we will need: Grid : the gaming world holding all of the locations. We should be able to change position on the grid. Training Course PHPUnit Nick Belhomme 2010 p. 10
  • 11. TRAINING COURSE PHPUNIT Tile: a location on the grid. This can be a room or for instance a jungle. We should be able to perform various actions on it and store objects in it. Item: an item on which various actions can be added in order to manipulate it. Also possibility to talk to the item. ItemCombination: Defines which two items can be used together. Action: an action, manipulation. Inventory: a list of items. Conversation: A conversation which can be linked to an item. All this classes will have their own prefix Game_ which will serve as a name space. It is always good practice to define each project and library with a specific name space / prefix. This way same name clashes will most likely not occur. Training Course PHPUnit Nick Belhomme 2010 p. 11
  • 12. TRAINING COURSE PHPUNIT Writing your first test case: Implementing the Grid First requirement is we will have to create a class called Game_Grid. Let's define this in a unit test. Tests/PHPUnit/Library/Game/GridTest.php <?php require_once 'PHPUnit/Framework.php'; require_once '../../../../Library/Game/Grid.php'; class Game_GridTest extends PHPUnit_Framework_TestCase { public function testConstructor() { $grid = new Game_Grid(); } } running the test will result a FATAL ERROR! Training Course PHPUnit Nick Belhomme 2010 p. 12
  • 13. TRAINING COURSE PHPUNIT This is because we didn't create the Game/Grid.php file yet OR we haven't setup our include path correctly OR the file Grid.php doesn't include a class of the type Game_Grid. Which can't be auto loaded. Once we have fixed these possible reasons we do not receive the fatal error anymore. Instead we get OK (1 test, 0 assertions) Let us analyze the unit test code. First we include our PHPUnit framework and our class to test with require statements. All Unit test cases are in the [class name]Test format. You take the class name to test and append it with Test. The class extends PHPUnit_Framework_TestCase which will give you access to the full power of Training Course PHPUnit Nick Belhomme 2010 p. 13
  • 14. TRAINING COURSE PHPUNIT PHPUnit. This includes various template methods such as setUp() and tearDown(), assertion methods, mocking, etc. We let PHPUnit find every test by sticking to the naming format that every function starting with the string test, is a test. So every test method starts with test appended with a string of choice. It is best to give a descriptive method name explaining what you are testing. In example: public function testGetDescriptionToReturnFalseOnInitialisation() PHPUnit finds all the tests by reflection. It searches for all methods of the format test* and will execute them in the order defined. You should make sure every test is independent of another. And finally all tests can have multiple assertions. In our test we didn't include any assertions yet but they will grow rapidly. So what did we do to fix the bug? 1) We first create our application structure It is good practice to separate the tests and the actual library into two distinct folders. We create a sub folder in Tests because several testing suites can be available for an application, PHPUnit is one of them. The test case classes in the Tests/PHPUnit directory (will) mirror the package and class structure of the System Under Test (SUT). Probably the easiest way to compose a test suite is to keep all test case source files in a test directory. PHPUnit can automatically discover and run the tests by recursively traversing the Training Course PHPUnit Nick Belhomme 2010 p. 14
  • 15. TRAINING COURSE PHPUNIT test directory. 2) Then we start by creating our unit test We created our first test called GridTest.php with the above Unit test embedded. All test cases respect the following file naming convention: [class-name-to-test] +“Test.php” 3) We execute the command: "phpunit GridTest.php" in the folder where the test is located. This will result in a fatal error: require_once... 4) We add the file Grid.php in the Library directory. 5) Re-run the test again and we will see it fail once more: Class not found Training Course PHPUnit Nick Belhomme 2010 p. 15 If you get an error that your operating system doesn't recognize phpunit, you haven't set your execution rights or the path environment variables correct. After installing PHPUnit you should be able to execute the phpunit command from everywhere in the file system.
  • 16. TRAINING COURSE PHPUNIT 6) Add the class definition into the Grid.php file. Library/Game/Grid.php <?php class Game_Grid {} 7) run the test once more and see it pass. Bootstrapping PHPUnit test cases Because a lot of basic functions are repetitive and should be loaded for each test we could bootstrap it. In the bootstrap we can for instance set the include path, or an auto loader. Or any other directive that your application needs for running correctly. You can load a bootstrap file by passing it as a parameter PHPUnit --bootstrap [bootstrap file] [test case to run] in example: PHPUnit --bootstrap Bootstrap.php GridTest.php In our test suite we will be bootstrapping our tests. This will include a basic auto loader so we do not have to write all the includes all the time. When we run our application itself we will also auto load it. Everything will be auto loaded in the application. So why should our unit tests be any different. Create a file called Bootstrap.php (you can call it whatever you want but it is always a good idea to stick to naming conventions. Because PHPUnit regards it as a bootstrap, you might as well name it Training Course PHPUnit Nick Belhomme 2010 p. 16
  • 17. TRAINING COURSE PHPUNIT that way). In the bootstrap we make sure our PHPUnit framework is loaded and implement a simple auto loader. <?php require_once 'PHPUnit/Framework.php'; function __autoload($className) { $path = explode('_', $className); $root = ''; if ($path[0] == 'App') { $root = '../../../../Application/Library/'; array_shift($path); } else if ($path[0] == 'Game') { $root = '../../../../Library/Game/'; array_shift($path); } require_once $root.implode(DIRECTORY_SEPARATOR, $path).'.php'; } Now that we have the auto loader in place we can remove the require_once from our tests. Training Course PHPUnit Nick Belhomme 2010 p. 17
  • 18. TRAINING COURSE PHPUNIT Configure PHPUnit with a phpunit.xml configuration file You can pass a lot of optional parameters to PHPUnit. For a complete list run the command without any test to run, any parameter or with the parameter --help PHPUnit PHPUnit --help We can use a configuration file to automagically pass parameters to the phpunit command. You can define which bootstrap to load and where to find it. Which tests should be run etc. PHPUnit will by default search for a file called phpunit.xml in the directory you run the command from. Of course you can name it any other file name. But we suggest you sticking to that. If you need multiple configuration files you can name them whatever you want. In example: phpunit.xml for normal testing phpunitCruisecontrol.xml for a special cruisecontrol setup which is a part of the continuous integration methodology. Let's take a closer look at the configuration file we will setup: Training Course PHPUnit Nick Belhomme 2010 p. 18
  • 19. TRAINING COURSE PHPUNIT Tests/PHPUnit/Library/Game/phpunit.xml <?xml version="1.0" encoding="utf-8"?> <phpunit bootstrap="./Bootstrap.php"> <testsuite name="Game"> <directory>./</directory> </testsuite> <logging> <log type="coverage-html" target="/tmp/PHPUnit/Coverage/" charset="UTF-8" yui="true", highlight="false" lowUpperBound="35" highLowerBound="70" /> </logging> </phpunit> This will tell PHPUnit you want the Bootstrap.php loaded as a bootstrap and the entire test suite called Game Test Suite is in the current directory and subfolders. Alternatively instead of setting a <directory> you can also specifically set the order using the <file> tag. The order in which you define the <file> tag is the order in which the tests will be executed. After specifying these configurations PHPUnit can find all tests automatically and you do not have to write PHPUnit --bootstrap Bootstrap.php GridTest.php anymore. The following command will suffice: PHPUnit Training Course PHPUnit Nick Belhomme 2010 p. 19 You do not have to specify the XML declaration for PHPUnit to understand it. But as always it is best practice.
  • 20. TRAINING COURSE PHPUNIT Training Course PHPUnit Nick Belhomme 2010 p. 20
  • 21. TRAINING COURSE PHPUNIT Writing Assertions We want to define the grid as a square which can hold Tiles. The Grid should be dynamic in size, take actions and store a position indicating where you are on the map. Let's first start implementing the constructor. We want it to accept the number of vertical tiles and horizontal tiles as integers. Then we want to be able to get the grid size set. We define the requirements by means of a Test. <?php public function testConstructorNormalParamsWithGetGridSize() { $x = rand(1,100); $y = rand(1,100); $grid = new Game_Grid($x,$y); $gridSize = $grid->getGridSize(); $this->assertEquals($x, $gridSize['x']); $this->assertEquals($y, $gridSize['y']); $grid = new Game_Grid((string) $x, (string) $y); $gridSize = $grid->getGridSize(); $this->assertEquals($x, $gridSize['x']); $this->assertEquals($y, $gridSize['y']); } We run the test: PHPUnit As you can see there is a fatal error: call to undefined method. Lets implement these requirements into Game_Class Training Course PHPUnit Nick Belhomme 2010 p. 21
  • 22. TRAINING COURSE PHPUNIT <?php class Game_Grid { protected $_grid; public function __construct($gridSizeX, $gridSizeY) { for ($x = 0; $x < (int) $gridSizeX; $x++) { for ($y = 0; $y < (int) $gridSizeY; $y++) { $this->_grid[$x][$y] = null; } } } public function getGridSize() { return array('x' => count($this->_grid), 'y' => count($this->_grid[0])); } } Testing for Exceptions and PHP Errors Okay so far so good. But what happens if we pass negative values or no integers at all? Time to put it to a test and make sure how code can handle these kind of situations. This is one of the fundamental rules in unit testing: check boundaries or abnormal situations. By thinking of ways your application might be used and putting it thru the test, you develop more robust and safer software. It also makes you understand your code and the requirements better. 1. Test for boundary conditions. 2. Test for both success and failure. Training Course PHPUnit Nick Belhomme 2010 p. 22
  • 23. TRAINING COURSE PHPUNIT 3. Test for general functionality. So let us extend the testCase with new tests and some more assertions. public function testConstructZeroParamsX() { $this->setExpectedException('Exception'); $grid = new Game_Grid(0,3); } public function testConstructZeroParamsY() { $this->setExpectedException('Exception'); $grid = new Game_Grid(2,0); } public function testConstructNegativeParamsX() { $this->setExpectedException('Exception'); $grid = new Game_Grid(-2,3); } /** * @expectedException Exception */ public function testConstructNegativeParamsY() { $grid = new Game_Grid(2,-3); } Here we define the requirement to throw an Exception when a negative parameter is given. You can tell the testing framework to expect an exception in two ways. * By setting a DocBlock with @expectedException [exception to expect] * By calling the setExpectedException($exceptionToExpect) method on the framework Both work equally well but you should choose one. Remember consistency makes reading much more comprehensible. The test will show us that this is not yet thrown by our implementation Training Course PHPUnit Nick Belhomme 2010 p. 23
  • 24. TRAINING COURSE PHPUNIT This will force us to write the following implementation: public function __construct($gridSizeX, $gridSizeY) { if ($gridSizeX <= 0 || $gridSizeY <= 0) { throw new Exception('The size of the Grid cannot be negative or zero'); } for ($x = 0; $x < (int) $gridSizeX; $x++) { for ($y = 0; $y < (int) $gridSizeY; $y++) { $this->_grid[$x][$y] = null; } } } Training Course PHPUnit Nick Belhomme 2010 p. 24 You can also test on PHP warnings for instance when you do type hinting for parameter checking. PHP will throw a warning when you define a function which expects a certain type but you provide another type. You can test that functionality with: $this->setExpectedException('PHPUnit_Framework_Error');. This is useful when you want your application to force the user to use a specific type. You can check if you have put this check in place.
  • 25. TRAINING COURSE PHPUNIT This time the test will succeed and you can move on to the next assertion you can think of. There are a lot of assertions to choose from. A complete list can be fount here: http://www.phpunit.de/manual/current/en/api.html#api.assert Training Course PHPUnit Nick Belhomme 2010 p. 25
  • 26. TRAINING COURSE PHPUNIT Incomplete and skipped tests Generally when you are working on a new test case class, you might want to begin by writing empty test methods for your public api such as: public function testGetTileWhenNoneIsSet() { $this->markTestIncomplete( 'This test has not been implemented yet.' ); } public function testGetTileFromPostionWhenNoTileIsSet() { $this->markTestIncomplete( 'This is a custom message.' ); } public function testAddTile() { $this->markTestIncomplete( 'This test has not been implemented yet.' ); } public function testAddTileOutsideGrid() { $this->markTestIncomplete( 'This test has not been implemented yet.' ); } public function testIsOnGrid() { $this->markTestIncomplete( 'This test has not been implemented yet.' ); } public function testPosition() { $this->markTestIncomplete( 'This test has not been implemented yet.' ); } public function testSetPositionOutsideGrid() { $this->markTestIncomplete( 'This test has not been implemented yet.' ); } public function testDefaultGridActions() { $this->markTestIncomplete( 'This test has not been implemented yet.' ); } public function testAddActionAndGetActions() { $this->markTestIncomplete( 'This test has not been implemented yet.' ); } This practice will make sure you do not forget to test some of your public api methods. PHPUnit will tell you on each run you did not yet implement the required test. Training Course PHPUnit Nick Belhomme 2010 p. 26
  • 27. TRAINING COURSE PHPUNIT Having an empty tests will not suffice. public function testSomething() { } Empty tests have the problem that they are interpreted as a success by the PHPUnit framework. This misinterpretation leads to the test reports being useless -- you cannot see whether a test is actually successful or just not yet implemented. Calling $this->fail() in the unimplemented test method does not help either, since then the test will be interpreted as a failure. This would be just as wrong as interpreting an unimplemented test as a success. To indicate that a certain test should be skipped we can use the method $this->markTestSkipped( 'The MySQLi extension is not available.' ); This is useful when some precondition is not met. For instance a mySQLi extention is not available. The descriptive messages passed as a parameter can be any string you like. Training Course PHPUnit Nick Belhomme 2010 p. 27
  • 28. TRAINING COURSE PHPUNIT Test Doubles We are going to add Tiles to our Game_Grid. First we add tests for getting the Tile. public function testGetTileWhenNoneIsSet() { $grid = new Game_Grid(1,1); $this->assertNull($grid->getTile(0,0)); } public function testGetTileOutsideGrid() { $this->setExpectedException('Exception'); $grid = new Game_Grid(1,1); $this->assertNull($grid->getTile(3,0)); } When we have implemented our Game_Grid class to make the tests OK we can proceed to add Tiles. How can we test this when the Game_Tile class hasn't been written yet? For this reason amongst others we have Mocks at our disposal. They are very powerful. public function testAddTile() { $tile = $this->getMock('stdClass', null, array(), 'Game_Tile'); $grid = new Game_Grid(1,1); $this->assertType('Game_Grid', $grid->addTile($tile, 0, 0)); $this->assertType('Game_Tile', $grid->getTile(0,0)); } At the moment there is no public api yet implemented for Game_Tile so Game_Grid cannot be aware of it and thus not use it. Mocking from stdClass will not work if there is interaction with Game_Tile from within the method Game_Grid::addTile(). When such an implementation is needed we will need to create the Game_Tile class and implement the called methods, properties or whatever is needed. Then we can use the Game_Tile class as a blueprint like we did with stdClass. Making sure no real actions are performed when called upon. “Sometimes it is just plain hard to test the system under test (SUT) because it depends on other components that cannot be used in the test environment. This could be because they aren't available, they will not return the results needed for the test or because executing them would have undesirable side effects. In other cases, our test strategy requires us to have more control or visibility of the internal behavior of the SUT. When we are writing a test in which we cannot (or chose not to) use a real depended-on component (DOC), we can replace it with a Test Double. The Test Double doesn't have to behave exactly like the real DOC; it merely has to provide the same API as the real one so that the SUT thinks it is the real one! [Gerard Meszaros - Meszaros2007] “ Training Course PHPUnit Nick Belhomme 2010 p. 28
  • 29. TRAINING COURSE PHPUNIT $this->getMock() method of PHPUnit accepts several parameters. protected function getMock($originalClassName, $methods = array(), array $arguments = array(), $mockClassName = '', $callOriginalConstructor = TRUE, $callOriginalClone = TRUE, $callAutoload = TRUE) As you can see only the first parameter is mandatory all others are optional. The first parameter is the name of an existing class needed to be mocked. PHPUnit will replace the implementation from all the methods from this class with “return null;” unless otherwise stated in parameter two or by further configuration of the mock object returned. The only method that will remain unchanged by default is the constructor which can also be mocked by setting the fifth parameter to false. As we said parameter two gives you the possibility to specify only those methods for which you want PHPUnit to change the implementation. Only the methods whose names are in the array are replaced with a configurable test double. The behavior of the other methods is not changed. The third parameter may hold a parameter array that is passed to the original class' constructor. The fourth parameter can be used to specify a class name for the generated test double class. The fifth parameter can be used to disable the call to the original class' constructor. The sixth parameter can be used to disable the call to the original class' clone constructor The seventh parameter can be used to disable __autoload() during the generation of the test double class. After implementing the tile functionality into the grid, we also need to implement the actions that can be set. We want to be able to move from tile to tile on the grid. This means we have to be able to add and get actions from Game_Grid. The tricky part in this implementation is that Game_Action should be aware on which Game_Item, Game_Tile and or Game_Grid object it is acting it's magic on. This was defined in the project requirements. This implies that when adding an action to the grid the grid sets itself as a subject to the action. (see class diagram) Stubbing is the practice of replacing an object with a test double that (optionally) returns configured return values. You can use a stub to "replace a real component on which the SUT depends so that the test has a control point for the indirect inputs of the SUT. This allows the test to force the SUT down paths it might not otherwise execute". Our implementation of Game_Action will be an abstract one. We could make it a fully implemented class which accepts a name by a setter. But we do not want that, because we feel like every action in the game should have a real hardcoded implementation and not generated on the fly. Of course this can change in time. In example future implementations may come from a CMS tool and then we will need the setters. But for now we do not want a mix of real objects with objects generated on the Training Course PHPUnit Nick Belhomme 2010 p. 29
  • 30. TRAINING COURSE PHPUNIT fly. First let us build the tests for Game_Action. To test Game_Action we will use the PHPUnit method PHPUnit_Testframework_TestCase::getMockForAbstractClass(). protected function getMockForAbstractClass($originalClassName, array $arguments = array(), $mockClassName = '', $callOriginalConstructor = TRUE, $callOriginalClone = TRUE, $callAutoload = TRUE) Which will instantiate an abstract class with a test double. Making sure our abstract execute is implemented. Tests/PHPUnit/Library/Game/ActionTest.php <?php class Game_ActionTest extends PHPUnit_Framework_TestCase { public function setUp() { $this->_action = $this->getMockForAbstractClass('Game_Action'); } public function testConstructorWithParams() { $grid = $this->getMock('Game_Grid', null, array(1,1)); $inventory = $this->getMock('Game_Inventory', null, array()); $this->assertType('Game_Action', $this->getMockForAbstractClass('Game_Action', array($grid, $inventory))); $this->assertType('Game_Action', $this->getMockForAbstractClass('Game_Action', array($grid))); $this->assertType('Game_Action', $this->getMockForAbstractClass('Game_Action', array(null, $inventory))); $this->assertType('Game_Action', $this->getMockForAbstractClass('Game_Action', array(null, null))); } public function testConstructorWithParamsNotGrid() { $this->setExpectedException('PHPUnit_Framework_Error'); $this->assertType('Game_Action', $this->getMockForAbstractClass('Game_Action', array('blabla'))); } public function testConstructorWithParamsNotInventory() { $this->setExpectedException('PHPUnit_Framework_Error'); $this->assertType('Game_Action', $this->getMockForAbstractClass('Game_Action', array(null, 'blabla'))); } Training Course PHPUnit Nick Belhomme 2010 p. 30
  • 31. TRAINING COURSE PHPUNIT public function testSetItem() { require_once 'Item/Stub.php'; $item = new Game_Item_Stub(); $this->assertType('Game_Action', $this->_action->setSubject($item)); $tile = $this->getMock('Game_Tile'); $this->assertType('Game_Action', $this->_action->setSubject($tile)); $grid = $this->getMock('Game_Grid', null, array(1,1)); $this->assertType('Game_Action', $this->_action->setSubject($grid)); } public function testSetItemIncorrectParam() { $this->setExpectedException('Exception'); $this->assertType('Game_Action', $this->_action->setSubject('blabla')); } public function testGetName() { $this->assertEquals('action', $this->_action->getName()); } public function testGetSynonyms() { $this->assertType('array', $this->_action->getSynonyms()); } public function testSetGrid() { $grid = $this->getMock('Game_Grid', null, array(1,1)); $this->assertType('Game_Action', $this->_action->setGrid($grid)); } public function testSetGridWithFaultyParam() { $this->setExpectedException('PHPUnit_Framework_Error'); $this->_action->setGrid('wrong param'); } public function testSetPersonalInventory() { $inventory = $this->getMock('Game_Inventory'); $this->assertType('Game_Action', $this->_action->setPersonalInventory($inventory)); } public function testSetPersonalInventoryWithFaultyParam() { $this->setExpectedException('PHPUnit_Framework_Error'); $this->_action->setPersonalInventory('wrong param'); } } Training Course PHPUnit Nick Belhomme 2010 p. 31
  • 32. TRAINING COURSE PHPUNIT Library/Game/Action.php <?php abstract class Game_Action { protected $_subject; protected $_name = 'action'; protected $_synonyms = array(); protected $_grid; protected $_personalInventory; public function __construct(Game_Grid $grid = null, Game_Inventory $inventory = null) { $this->_grid = $grid; $this->_personalInventory = $inventory; $this->_init(); } public function setSubject($subject) { if (!($subject instanceOf Game_Item) && !($subject instanceOf Game_Tile) && !($subject instanceOf Game_Grid)) { throw new Exception('Subject passed should be of type Game_subject, Game_Tile, Game_Grid'); } $this->_subject = $subject; return $this; } public function getName() { return $this->_name; } public function getSynonyms() { return $this->_synonyms; } public function setGrid(Game_Grid $grid) { $this->_grid = $grid; return $this; } public function setPersonalInventory(Game_Inventory $inventory) { $this->_personalInventory = $inventory; return $this; } abstract public function execute(); protected function _getExecutedMessageSuccess() { Training Course PHPUnit Nick Belhomme 2010 p. 32
  • 33. TRAINING COURSE PHPUNIT echo 'action executed'; } protected function _getExecutedMessageFailed() { echo 'action failed'; } protected function _executeSuccess() { // template method } protected function _executeFailed() { // template method } protected function _init() { // template method } } Now that Game_Action is implemented and tested it is time to create a helper stub. Because all the objects depending on actions need an action name to differentiate between the different actions by use of array keys, we will create a stub that will extend Game_Action and implement a setter. Game_Grid is such an object. We want to give it four moving actions: GoNorth, goSouth, goWest, goEast. To do this we must be able to change the name of the action. The stub Game_Action_Stub will extend the Game_Action abstract class, set the default name and add a setter. Tests/PHPUnit/Library/Game/Action/Stub.php <?php class Game_Action_Stub extends Game_Action { public function execute() { return null; } // helper function for testing public function setName($name) { $this->_name = $name; } } Training Course PHPUnit Nick Belhomme 2010 p. 33
  • 34. TRAINING COURSE PHPUNIT After having Game_Action in place we can continue testing Game_Grid. public function testAddActionAndGetActions() { require_once './Action/Stub.php'; $action = $this->getMock('Game_Action_Stub'); $action->expects($this->once()) ->method('setGrid') ->with($this->isInstanceOf('Game_Grid')); $action->expects($this->once()) ->method('getName') ->will($this->returnValue('stubAction')); $grid = new Game_Grid(2,2); $actions = $grid->getActions(); $initialActionsCount = count($actions); $this->assertType('Game_Grid', $grid->addAction($action)); $actions = $grid->getActions(); $this->assertEquals($initialActionsCount+1, count($actions)); // Same name, it will overwrite the previous one set $action2 = $this->getMock('Game_Action'); $action2->expects($this->once()) ->method('getName') ->will($this->returnValue('stubAction')); $this->assertType('Game_Grid', $grid->addAction($action2)); $actions = $grid->getActions(); $this->assertEquals($initialActionsCount+1, count($actions)); } We define in the test: 1) there should be a method Game_Grid::addAction and it should make one call to the method setGrid on the passed Game_Action instance passing the Game_Grid as a param. 2) Secondly it also should make a call to Game_Action::getName() and this method will return the string 'stubAction'. Thus forcing Game_Grid to accept a string value. Which it will actually use as the key in the $actions array. 3) It also should have a fluent interface so the return value of addAction should be Game_Grid itself. 4) Adding a new action will resolve in one action registered. Adding a second with the same name will overwrite the previous one added. This requirement is tested by creating another action with the same name and adding it to the Grid. public function addAction(Game_Action $action) { $this->_actions[$action->getName()] = $action; $action->setGrid($this); return $this; } Training Course PHPUnit Nick Belhomme 2010 p. 34
  • 35. TRAINING COURSE PHPUNIT Training Course PHPUnit Nick Belhomme 2010 p. 35 You cannot test the call to Game_Action::setGrid if you use getMockForAbstractClass(). This method replaces the abstract methods by a default implementation, leaving the others unchanged. It will always return failure because it cannot register the call. Thus we use getMock() which needs a non abstract class. Luckily for us we already defined the helper stub class earlier.
  • 36. TRAINING COURSE PHPUNIT Dependencies Tests/PHPUnit/Library/Game/GridTest.php <?php class Game_GridTest extends PHPUnit_Framework_TestCase { public function testConstructorNormalParamsWithGetGridSize() { $x = rand(1,100); $y = rand(1,100); $grid = new Game_Grid($x,$y); $gridSize = $grid->getGridSize(); $this->assertEquals($x, $gridSize['x']); $this->assertEquals($y, $gridSize['y']); $grid = new Game_Grid((string) $x, (string) $y); $gridSize = $grid->getGridSize(); $this->assertEquals($x, $gridSize['x']); $this->assertEquals($y, $gridSize['y']); } public function constructorProviderBadParams() { return array( array(0, 0), array(0, 1), array(1, 0), array(-1, 1), array(1, -1), array(-1, -1), ); } /** * * @dataProvider constructorProviderBadParams */ public function testConstructNegativeOrZeroParam($paramX, $paramY) { $this->setExpectedException('Exception'); $grid = new Game_Grid($paramX, $paramY); } public function testGetTileWhenNoneIsSet() { $grid = new Game_Grid(1,1); $this->assertNull($grid->getTile(0,0)); } public function testGetTileFromPostionWhenNoTileIsSet() { $grid = new Game_Grid(1,1); $this->assertNull($grid->getTileFromPosition()); } public function testAddTile() Training Course PHPUnit Nick Belhomme 2010 p. 36
  • 37. TRAINING COURSE PHPUNIT { $tile = $this->getMock('Game_Tile', null, array(), 'Game_Tile_Mock', false); $grid = new Game_Grid(1,1); $this->assertType('Game_Grid', $grid->addTile($tile, 0, 0)); $this->assertType('Game_Tile_Mock', $grid->getTile(0,0)); } public function testAddTileOutsideGrid() { $this->setExpectedException('Exception'); $tile = $this->getMock('Game_Tile', null, array(), 'Game_Tile_Mock', false); $grid = new Game_Grid(1,1); $grid->addTile($tile, 2, 3); } public function testIsOnGrid() { $grid = new Game_Grid(1,1); $this->assertTrue($grid->isOnGrid(0,0)); $this->assertFalse($grid->isOnGrid(0,1)); } public function testPosition() { $x = rand(1,100); $y = rand(1,100); $grid = new Game_Grid($x,$y); $position = $grid->getPosition(); $this->assertEquals(0, $position['x']); $this->assertEquals(0, $position['y']); $newPositionX = rand(0,$x-1); $newPositionY = rand(0,$y-1); $grid->setPosition($newPositionX, $newPositionY); $position = $grid->getPosition(); $this->assertEquals($newPositionX, $position['x']); $this->assertEquals($newPositionY, $position['y']); } public function testSetPositionOutsideGrid() { $this->setExpectedException('Exception'); $grid = new Game_Grid(1,1); $grid->setPosition(3,2); } public function testDefaultGridActions() { $grid = new Game_Grid(2,2); $actions = $grid->getActions(); $this->assertEquals(4, count($actions)); $gameActionGoNorthFound = false; $gameActionGoEastFound = false; $gameActionGoSouthFound = false; Training Course PHPUnit Nick Belhomme 2010 p. 37
  • 38. TRAINING COURSE PHPUNIT $gameActionGoWestFound = false; foreach ($actions as $action) { if ($action instanceof Game_Action_Go_North) { $gameActionGoNorthFound = true; } if ($action instanceof Game_Action_Go_East) { $gameActionGoEastFound = true; } if ($action instanceof Game_Action_Go_South) { $gameActionGoSouthFound = true; } if ($action instanceof Game_Action_Go_West) { $gameActionGoWestFound = true; } } $this->assertTrue($gameActionGoNorthFound); $this->assertTrue($gameActionGoEastFound); $this->assertTrue($gameActionGoSouthFound); $this->assertTrue($gameActionGoWestFound); return $grid; } /** * this dependency has been added to show this functionality * @depends testDefaultGridActions */ public function testAddActionAndGetActions(Game_Grid $grid) { require_once './Action/Stub.php'; $action = $this->getMock('Game_Action_Stub'); $action->expects($this->once()) ->method('setGrid') ->with($this->isInstanceOf('Game_Grid')); $action->expects($this->once()) ->method('getName') ->will($this->returnValue('stubAction')); $actions = $grid->getActions(); $this->assertEquals(4, count($actions)); $this->assertType('Game_Grid', $grid->addAction($action)); $actions = $grid->getActions(); $this->assertEquals(5, count($actions)); // Same name, it will overwrite the previous one set $action2 = $this->getMock('Game_Action'); $action2->expects($this->once()) ->method('getName') ->will($this->returnValue('stubAction')); $this->assertType('Game_Grid', $grid->addAction($action2)); $actions = $grid->getActions(); $this->assertEquals(5, count($actions)); } } Normally every test should be independent from each other. And should be able to be executed in random order. Yet sometimes to quickly localize defects, we want our attention to be focused on Training Course PHPUnit Nick Belhomme 2010 p. 38
  • 39. TRAINING COURSE PHPUNIT relevant failing tests. This is why PHPUnit skips the execution of a test when a depended-upon test has failed. This improves defect localization by exploiting the dependencies between tests as shown in the above testcase, “Exploiting the dependencies between tests”. Purely functioning as an example we have changed some tests in the above code. The test testAddActionAndGetActions depends on testDefaultGridActions. Because if the defaultGridActions are not set correctly then our assertEquals for adding and getting will also fail for testAddActionAndGetActions. We can define such a dependency by using a doc block annotation @depends [test on which the test is dependant]. We have the possibility to pass fixtures as params in such cases, making sure that the test environment is correctly set. For the above dependency, testDefaultGridActions is called the producer and testAddActionAndGetActions is called the consumer. The DataProvider Sometimes we want to test the same method with different params which should all result in the an outcome predicted by the test. To facilitate this a dataProvider has been made available. In the code in the previous chapter we have rewritten the tests from the chapter: “Testing for Exceptions and PHP Errors” to use the dataProvider annotation. This will make our testing much more easy to understand and extend. Because it removes redundancy. For each array that is part of the dataProvider collection the test method will be called with the contents of the array as its arguments. You create a public method which returns an array or an object which implements the Iterator interface. This method is called the dataProvider and you link it to the testmethod with a doc block @dataProvider [provider method] Training Course PHPUnit Nick Belhomme 2010 p. 39 A producer is a test method that yields its unit under test as return value. A consumer is a test method that depends on one or more producers and their return values. PHPUnit does not change the order in which tests are executed, you have to ensure that the dependencies of a test can actually be met before the test is run.
  • 40. TRAINING COURSE PHPUNIT Training Course PHPUnit Nick Belhomme 2010 p. 40 When a test receives input from both a @dataProvider method and from one or more tests it @depends on, the arguments from the data provider will come before the ones from depended-upon tests.
  • 41. TRAINING COURSE PHPUNIT setUp, tearDown and other template methods One of the most time-consuming parts of writing tests is writing the code to setup the world to a known state and then return it to its original state when the test is complete. This known state is called the fixture of the test. Setting and destructing a fixture for every test can be time consuming or a lot of copy and pasting, the latter being against good coding practice. DRY (Do Not Repeat yourself) is something every programmer should try to adhere to. PHPUnit has made it easy for us to to provide us with hooks in the form of template methods. Template methods are empty methods which are called in a predefined algorithm set by the class which owns the template methods. You may give the algorithm a specific implementation by implementing the methods. Template methods available in order of execution. • setUpBeforeClass(): Each time when your testcase class is run this method will be called once. Here you can create for example a file with content that will not change in the life cycle of the tests. And there for is not needed to be created every time between tests. • setUp(): Before a test method is run, a template method called setUp() is invoked. setUp() is where you create the objects against which you will test. • assertPreConditions(): After each setup but before each test, this method is called. Here you can implement a check on certain aspects of your testing stage. [THE ACTUAL TEST WILL BE EXECUTED HERE] • assertPostConditions(): After each test executed, this method is called. Here you can implement a check on certain aspects of your testing stage. • tearDown(): Once the test method has finished running, whether it succeeded or failed, another template method called tearDown() is invoked. tearDown() is where you clean allocated external resources like files or sockets. Also clean up objects for garbage collection. • tearDownAfterClass(): Each time when your testcase class has finished running all tests and template methods this last method will be called. This method will be called once. Here you can delete for example the file that was set in the setupBeforeClass(). Training Course PHPUnit Nick Belhomme 2010 p. 41
  • 42. TRAINING COURSE PHPUNIT Testing your tests, code coverage After creating a test for some functionality you should test the testcase for code coverage. What use do tests have if they do not cover all of your application code. They will provide you with a false sense of safety. Thus it is very important to tests your tests. Luckily for us, PHPUnit has such a feature embedded into it's service. It uses the Xdebug extension. PHPUnit can output code coverage in the following formats: • html: coverage report in HTML format phpunit --coverage-html <dir> • clover: code coverage data in Clover XML format phpunit --coverage-clover <file> • source: code coverage / source data in XML format phpunit --coverage-source <dir> To produce a code coverage test for ActionTest use the following command. phpunit --coverage-html /tmp/PHPUnit/Coverage/ ActionTest We have already defined the setup for the code coverage in our configuration file from chapter “Configure PHPUnit with a phpunit.xml configuration file” <logging> <log type="coverage-html" target="/tmp/PHPUnit/Coverage/" charset="UTF-8" yui="true", highlight="false" lowUpperBound="35" highLowerBound="70" /> </logging> The <logging> element and its <log> children can be used to configure the logging of the test execution. type: specifies the type of logging target: the target directory where the log should be written to charset: the character encoding for the log yui: Yahoo User Interface, true enables javascript on click events. Highlight: activates the code syntax highlighting. lowUpperBound: overwrite the default percentage of 35 to your upper limit of max coverage percentage that is needed to qualiy as low. HighLowerBound: overwrite the default percentage of 70 to your minimum coverage percentage is needed to qualify as high coverage. Training Course PHPUnit Nick Belhomme 2010 p. 42
  • 43. TRAINING COURSE PHPUNIT Increase the readability by creating smaller tests and adding assertion messages. Want to know more? Want to learn about all the nitty gritty hidden features of PHPUnit? Want to integrate PHPUnit in your IDE? Want to integrate PHPUnit in your ZF projects? Want to fully automate the running of tests with continuous integration? GOOD NEWS, YOU CAN! • I give workshops at community events for free (technically they are sponsored) • I can give a workshop at the company for which you work. (Your boss pays and during office hours you get to become a PHPUnit expert, pretty neat I would say) • Purchase my upcoming book on an important framework. THANK YOU FOR READING, Nick Belhomme follow me on twitter: @NickBelhomme http://nickbelhomme.com/phpunit-training Training Course PHPUnit Nick Belhomme 2010 p. 43