Tuesday, 29 January 2013

PHPUnit

PHPUnit provides a simple framework for creating a test suite to automate testing of functions and classes.

PHPUnit  is for to test small software components as often and early as possible, this way we will not have to fix bugs and errors in the API while setting up and testing larger applications which depend on the class.

PHPUnit stands alone as a good tool for testing classes or a set of functions and will ease your development cycle and help you to avoid endless debug sessions.

WORK UNIT

Normally, we would write a class, do some unsystematic tests using echo() or var_dump() or print_r(). After this, we use the class in your application and hope everything is ok. To benefit from PHPUnit you should rethink the flow. The best way is to do this:
  • 1. design your class/API
  • 2. create a test suite
  • 3. implement the class/API
  • 4. run the test suite
  • 5. fix failures or errors and go to #4 again
It may seem that this will require a lot of time, but this impression is wrong. Creating the test suite using PHPUnit needs only a few minutes and running the test suite only seconds.


INSTALLATION 

Installing PHPUnit

There a three supported ways of installing PHPUnit. You can use the PEAR Installer or Composer to download and install PHPUnit as well as its dependencies. You can also download a PHP Archive (PHAR) of PHPUnit that has all required (as well as some optional) dependencies of PHPUnit bundled in a single file.

PHPUnit 3.7 requires PHP 5.3.3 (or later) but PHP 5.4.7 (or later) is highly recommended.

We are linux user so we’ve used PEAR to install PhpUnit, If PEAR is not available so can install PEAR by following commands.

Installing PEAR 

Getting and installing the PEAR package manager

1.  If Pear is not available
Install PEAR by using following cammands

wget http://pear.php.net/go-pear.phar
php go-pear.phar


After installing PEAR we can now Install PHPUnit

For installing PHPUnit use the following commands

pear config-set auto_discover 1
pear install pear.phpunit.de/PHPUnit


Out put
install ok: channel://pear.phpunit.de/File_Iterator-1.3.3
install ok: channel://pear.phpunit.de/Text_Template-1.1.4
install ok: channel://pear.phpunit.de/PHP_Timer-1.0.4
install ok: channel://pear.symfony.com/Yaml-2.1.7
install ok: channel://pear.phpunit.de/PHP_TokenStream-1.1.5
install ok: channel://pear.phpunit.de/PHP_CodeCoverage-1.2.7
install ok: channel://pear.phpunit.de/PHPUnit_MockObject-1.2.3
install ok: channel://pear.phpunit.de/PHPUnit-3.7.13

The above will install PhpUnit




Explaining how to design Class and writing and running the test suits for Class.


Real example of PHPUnit

We are taking a simple example to explain how to writing and running the test suits for Class.


Design a class

---- string.php ----
<?php
class String
{
   //contains the internal data
   var $data;

   // constructor
   function String($data) {
       $this->data = $data;
   }

   // creates a deep copy of the string object
   function copy() {
   }

   // adds another string object to this class
   function add($string) {
   }
   // returns the formated string
   function toString($format) {
   }
}?>

Creating test suite

Now we can create a test suite, which checks every function of your string class. A test suite is normal PHP class inherited from PHPUnit_TestCase containing test functions, identified by a leading 'test' in the function name. In the test function an expected value has to be compared with the result of the function to test. The result of this compare must delegate to a function of the assert*()-family, which decides if a function passes or fails the test.


---- testcase.php ----

<?php
require_once 'string.php';

class StackTest extends PHPUnit_Framework_TestCase
{
   
   // contains the object handle of the string class
   var $abc;
   
   // constructor of the test suite
   public function StringTest($name) {
       $this->PHPUnit_Framework_TestCase($name);
   }
   
   // called before the test functions will be executed
   // this function is defined in PHPUnit_TestCase and overwritten
   // here
   public function setUp() {
       // create a new instance of String with the
       // string 'abc'
       $this->abc = new String("abc");
   }
   
   // called after the test functions are executed
   // this function is defined in PHPUnit_TestCase and overwritten
   // here
     public function tearDown() {
       // delete your instance
       unset($this->abc);
   }
   
   public function testToString() {
       $result = $this->abc->toString('contains %s');
       $expected = 'contains abc';
       $this->assertTrue($result == $expected);
   }
   
   // test the copy function
   public function testCopy() {
       $abc2 = $this->abc->copy();
       $this->assertEquals($abc2, $this->abc);
   }
   
   // test the add function
   public function testAdd() {
       $abc2 = new String('123');
       $this->abc->add($abc2);
       $result = $this->abc->toString("%s");
       $expected = "abc123";
       $this->assertTrue($result == $expected);
   }
   
   //$this->assertEquals('foosss', $stack[count($stack)-1]);
  
}
?>

The first test run
Run it from Command Line By using below  make sure you are on same path where this file is

phpunit testcase.php

If you call this script from the commandline, you will get the following output:

FFF

Time: 0 seconds, Memory: 2.50Mb

There were 3 failures:

1) StackTest::testToString
Failed asserting that false is true.

/var/www/html/phpunit/testcase.php:35

2) StackTest::testCopy
String Object (...) does not match expected type "NULL".

/var/www/html/phpunit/testcase.php:41

3) StackTest::testAdd
Failed asserting that false is true.

/var/www/html/phpunit/testcase.php:50

FAILURES!
Tests: 3, Assertions: 3, Failures: 3.

Every function fails the test, because your string functions didn't returned what we defined as the expected value.

Now you can make changes in your script.php as below

<?php
class String
{
    //contains the internal data
    var $data;

    // constructor
    function String($data) {
       $this->data = $data;
    }

    // creates a deep copy of the string object
    function copy() {
       $ret = new String($this->data);
       return $ret;
    }

    // adds another string object to this class
    function add($string) {
       $this->data = $this->data.$string->toString("%s");
    }

    // returns the formated string
    function toString($format) {
       $ret = sprintf($format, $this->data);
       return $ret;
    }
}
?>


And try Run the test case again,

phpunit testcase.php

the output would be

PHPUnit 3.7.13 by Sebastian Bergmann.
...

Time: 0 seconds, Memory: 2.50Mb

OK (3 tests, 3 assertions)


The Command-Line Test Runner
To run any test case we need to use “phpunit file name”  command.

As we run this command we will see below result

PHPUnit 3.7.0 by Sebastian Bergmann.
..
Time: 0 seconds
OK (2 tests, 2 assertions)



Sebastian Bergmann is the creator of PHPUnit
.   => Printed when the test succeeds.
F => Printed when an assertion fails while running the test method.
E => Printed when an error occurs while running the test method.
S => Printed when the test has been skipped.
I => Printed when the test is marked as being incomplete or not yet implemented.

Database Testing:

DB testing  requires extension DbUnit.

The DbUnit extension considerably simplifies the setup of a database for testing purposes and allows you to verify the contents of a database after performing a series of operations.
It can be installed like this:

pear install phpunit/DbUnit

downloading DbUnit-1.2.2.tgz ...

Starting to download DbUnit-1.2.2.tgz (41,888 bytes)
............done: 41,888 bytes
install ok: channel://pear.phpunit.de/DbUnit-1.2.2

DbUnit currently supports MySQL, PostgreSQL, Oracle and SQLite.

Configuration of a PHPUnit Database TestCase

If you want to test code that works with the Database Extension than require you to implement two abstract methods getConnection() and getDataSet().

which resides in PHPUnit/Extensions/Database/TestCase.php

So we have to include  “ PHPUnit/Extensions/Database/TestCase.php” file
require_once "PHPUnit/Extensions/Database/TestCase.php


require_once "PHPUnit/Extensions/Database/TestCase.php";

class MyGuestbookTest extends PHPUnit_Extensions_Database_TestCase
{
   /**
    * @return PHPUnit_Extensions_Database_DB_IDatabaseConnection
    */
   public function getConnection()
   {
       $pdo = new PDO("mysql:host=localhost;dbname=bulletproof",

               "root", "neova123");
       return $this->createDefaultDBConnection($pdo,
               "bulletproof");    }

   /**
    * @return PHPUnit_Extensions_Database_DataSet_IDataSet
    */
   public function getDataSet()
   {
       return $this->createXMLDataSet("seed.xml");

   }
}


getConnection(): This is to get connected with Database.

getDataSet():The getDataSet() method defines how the initial state of the database should look before each test is executed.


Example:
public function testSaveArticle() {
       $article = new ArticleDAO();
       
       $article->save(array(
               "title" => "PHP",
               "description" => "PHP",
               "content" => "PHP",
               "preview_image" => "PHP",
               "section_id" => "PHP",
               
       ));
   
       $resultingTable = $this->getConnection()->createQueryTable("articles","SELECT * FROM articles");
   
       $expectedTable =this->createXmlDataSet("expectedArticles.xml")->getTable("articles");
       $this->assertTablesEqual($expectedTable,$resultingTable);
   }




I have a BankAccount Example where we can be prity clear how and where to write Test cases in a real scenarios.

<?php

require_once 'BankAccount.php';

class BankAccountTest extends PHPUnit_Framework_TestCase
{
  protected $ba;

  protected function setUp()
  {
      $this->ba = new BankAccount;
  }

  public function testBalanceIsInitiallyZero()
  {
      $this->assertEquals(0, $this->ba->getBalance());
  }

  public function testBalanceCannotBecomeNegative()
  {
      try {
          $this->ba->withdrawMoney(1);
      }

      catch (BankAccountException $e) {
          $this->assertEquals(0, $this->ba->getBalance());

          return;
      }

      $this->fail();
  }

  public function testBalanceCannotBecomeNegative2()
  {
      try {
          $this->ba->depositMoney(-1);
      }

      catch (BankAccountException $e) {
          $this->assertEquals(0, $this->ba->getBalance());

          return;
      }

      $this->fail();
  }
}
?>



Skeleton Generator

The PHPUnit Skeleton Generator is a tool that can generate skeleton test classes from production code classes and vice versa. It can be installed using the following command:

pear install phpunit/PHPUnit_SkeletonGenerator

Example calculator.php

<?php
class Calculator
{
  public function add($a, $b)
  {
      return $a + $b;
  }
}
?>

by using below command you can generate skeleton for calculator.php

phpunit-skelgen --test Calculator

This will create a new file named  calculatorTest.php

<?php
/**
* Generated by PHPUnit_SkeletonGenerator 1.2.0 on 2013-01-28 at 11:42:17.
*/
class calculatorTest extends PHPUnit_Framework_TestCase
{
    /**
    * @var calculator
    */
    protected $object;

    /**
    * Sets up the fixture, for example, opens a network connection.
    * This method is called before a test is executed.
    */
    protected function setUp()
    {
       $this->object = new calculator;
    }

    /**
    * Tears down the fixture, for example, closes a network connection.
    * This method is called after a test is executed.
    */
    protected function tearDown()
    {
    }

public function testAdd() {
       $o = new Calculator;
       $this->assertEquals(0, $o->add(0, 0));
    }
}