Unit testing
Junior programmers often do not see the point in testing their own code with unit tests. Some of them believe that testing - is the work of testers. Some argue that tests are only needed for large projects. Others believe that tests are too difficult, and writing them takes too much time. There are some, who believe that their code is perfect and does not require testing.
Let's try to understand how to write tests so that they bring only benefits. First of all let’s understand what is unit testing and what types of unit tests there are. Unit testing is a method by which individual units of source code are tested to determine if they are fit for use.
Globally, unit tests can be divided into two groups:
State based unit-tests - tests that verify that the calling method or function works correctly. The goal of state-based unit tests is to isolate each part of the program and show that the individual parts are correct.
Interaction unit-tests - tests that verify that the test object interacts with the other objects correctly.
<?php
class Math
{
/** * Return sum * @param numeric $a * @param numeric $b * @return numeric */
public function add($a, $b)
{ $sum = $a + $b;
return $sum; } }
Let’s write an automated unit test to verify our method
<?php
require_once dirname(__FILE__) . '/../Classes/Math.php'; function assertEquals($expected, $actual)
{ if ($expected !== $actual) { throw new Exception('Assertion failed'); } echo('Assertion success' . PHP_EOL); } //GIVEN $a = 2; $b = 3; //WHEN $math = new Math(); $c = $math->add($a, $b); //THEN assertEquals(5, $c);
We check that the method of summing the numbers 2 and 3 returns the expected value of 5.
Prevention of errors
It is easy to see that our class has some defects. One of them is lack of validation of incoming parameters. Check how the object responds to the different types of data passed to it.
Add the following test case:
<?php
require_once dirname(__FILE__) . '/../Classes/Math.php'; function assertEquals($expected, $actual)
{ if ($expected !== $actual) { throw new Exception('Assertion failed'); } echo('Assertion success' . PHP_EOL); } //GIVEN $a = new stdClass(); $b = 3; //WHEN $math = new Math(); $c = $math->add($a, $b); //THEN assertEquals(3, $c);
If we run the test. we will see a notice:
The test helped us to detect an error, which we probably did not know of at the time of writing the class.
This will force us to add some check input parameters:
<?php
class Math
{
/** * Return sum * @param numeric $a * @param numeric $b * @return numeric
* @throws InvalidArgumentException
*/
public function add($a, $b)
{ if (!is_numeric($a) || !is_numeric($b)) { throw new InvalidArgumentException('Wrong parameters given'); } $sum = $a + $b; return $sum; } }
Facilitates change
Source code, like any system that has its life cycle, is necessary to be maintained, modified and improved. Unit testing allows the programmer to refactor the code at a later date, and make sure the module still works. If the programmer during refactoring accidentally puts a minus instead of plus, the test will immediately react with the anger message, and the company will not incur losses.
Simplifies integration
Over time the project extends, there is a new functionality. The process of integrating a new functionality is often a time-consuming task, since it is not known how the new functionality will affect the existing one. In this situation, tests will help us. Unit testing may reduce uncertainty in the units themselves and can be used in the bottom-up testing style approach. Testing the parts of a program first, and then testing the sum of its parts, simplifies integration testing a lot.
Documentation
It is often difficult to understand how a module works. Often, over time the author of the module can not remember how to integrate it into the system. Tests will help us again. Developers looking to learn what functionality is provided by a unit and how to use it, can look at unit tests to gain a basic understanding of the unit's API. Unit testing provides a sort of living documentation of the system.
Testing Frameworks
It is generally possible to write unit tests without the support of a specific framework. You have to write code that uses assertions, exception handling, or other control flow mechanisms to signal failure. Frameworks help simplify the process of unit testing.
There are a lot of test rameworks for PHP language. The most famous of them are:
PHPUnit
PHPUnit is the de-facto standard for unit testing in PHP projects. Created by Sebastian Bergmann, PHPUnit is one of the xUnit family of frameworks that originated with Kent Beck's SUnit. PHPUnit is hosted at GitHub. It provides both a framework that makes the writing of tests easy as well as the functionality to easily run the tests and analyse their results.
PHPUnit should be installed using the PEAR Installer, the backbone of the PHP Extension and Application Repository that provides a distribution system for PHP packages.
Depending on your OS distribution and/or your PHP environment, you may need to install PEAR or update your existing PEAR installation. The PEAR Manual explains how to perform a fresh installation of PEAR.
The following two commands (which you may have to run as root) are all that is required to install PHPUnit using the PEAR Installer:
$ sudo pear config-set auto_discover 1 $ sudo pear install --alldeps pear.phpunit.de/PHPUnit
Lets write the test using PHPUnit. Bе attentive - each test case class has “Test” in the end of class name, each test method begins with “test”
<?php
require_once dirname(__FILE__) . '/../Classes/Math.php'; class AddTest extends PHPUnit_Framework_TestCase
{ public function testShouldAddTwoNumbers()
{ //GIVEN $a = 2; $b = 3; //WHEN $math = new Math(); $c = $math->add($a, $b); //THEN $this->assertEquals(5, $c); } }
Save this file with name AddTest.php. Open console and run our test
If an error occurred, the test will tell you about it. For example, if we put a minus instead of plus in the Math::add()