Screencast Series – Creating Own PHP Framework Using Symfony2 Components – Episode 8

Save you Easter – Write Tests!

In this episode of the series we are going to ensure the durability of our code by including one of the most important components of the modern application design – Unit Testing.

Configuration

One of the advantages in using Symfony2 Components is their native support of test. For example, we’re going to create a configuration file for testing, and the autoloader will automatically take care of the bootstraping process for us.

		<?xml version="1.0" encoding="UTF-8"?>

        <phpunit 
                 colors="true"
                 bootstrap="vendor/.composer/autoload.php">
            <testsuites>
                <testsuite name="Test Suite of Simplex framework">
                    <directory>./tests</directory>
                </testsuite>
            </testsuites>
        </phpunit>

Using Test Doubles

We want to test our classes, but want to avoid creating numerous dependencies to replicate the interaction of the classes in our framework’s architecture. PHPUnit comes with the concept of test doubles. These are easier to create when our architecture relies on interfaces instead of concrete classes. The handy thing for us – Symfony2 Components URL Matcher and Controller Resolver have the support for this feature out of the box! Let’s replace the type hinting in our Framework to support interfaces: (UPDATE – PHPUnit seems to generate the Interface Names – something in the lines of UrlMatcherInterface_454332ae is used as a type. So since we can’t use the exact name of the interface, I have excluded the type hinting alltogether. We’ll be glad if someone will get me a hint how to resolve this issue.)

<?php
    // /src/Simplex/Framework.php
...
    class Framework
   {
       protected $matcher;
       protected $resolver;

       public function __construct($matcher,  $resolver)
       {
           $this->matcher = $matcher;
           $this->resolver = $resolver;
       }
...

The First Tests

We’re going to test the feature of our framework that takes care of the resources that were not found, for example someone is trying to type in a non-existent URL. As you might recall we rely on ResourceNotFoundException to deal with the URLs that are false. Let’s write a test to be sure we could sleep well at nights:

<?php
// /tests/Simplex/Tests/FrameworkTest.php

namespace Simplex\Tests;

use Simplex\Framework;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
use Symfony\Component\HttpKernel\Controller\ControllerResolver;

class FrameworkTest extends \PHPUnit_Framework_TestCase
{
   public function testNotFoundHandling()
   {

       $framework = $this->getFrameworkForException(new ResourceNotFoundException());

       $response  = $framework->handle(new Request());

       $this->assertEquals(404, $response->getStatusCode());

   }
   
      private function getFrameworkForException($exception)
   {

       $matcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface');

       $matcher
        ->expects($this->once())
        ->method('match')
        ->will($this->throwException($exception));

       $resolver = $this->getMock('Symfon\Component\HttpKernel\Controller\ControllerResolverInterface');

       return new Framework($matcher, $resolver);

   }

}

We’ll run the test

$ phpunit

The Complete Test Suite

<?php
// /tests/Simplex/Tests/FrameworkTest.php
namespace Simplex\Tests;

use Simplex\Framework;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
use Symfony\Component\HttpKernel\Controller\ControllerResolver;

class FrameworkTest extends \PHPUnit_Framework_TestCase
{
   // Testing 404
   public function testNotFoundHandling()
   {

       $framework = $this->getFrameworkForException(new ResourceNotFoundException());

       $response  = $framework->handle(new Request());

       $this->assertEquals(404, $response->getStatusCode());

   }

  // Testing 500
   public function testErrorHandling()
   {
       $framework = $this->getFrameworkForException(new \RuntimeException());

       $response = $framework->handle(new Request());

       $this->assertEquals(500, $response->getStatusCode());
   }

  // Testing 200
   public function testControllerResponse()
   {
       $matcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface');
       $matcher
        ->expects($this->once())
        ->method('match')
        ->will($this->returnValue(array(
           '_route'      => 'testing',
           'name'        => 'Andrey',
           '_controller' => function ($name) {
               return new Response('Hello ' . $name[0]);
           }
       )));

       $resolver = new ControllerResolver();

       $framework = new Framework($matcher, $resolver);

       $response = $framework->handle(new Request());

       $this->assertEquals(200, $response->getStatusCode());
       $this->assertContains('Hello Andrey', $response->getContent());
   }

   private function getFrameworkForException($exception)
   {

       $matcher = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface');

       $matcher
        ->expects($this->once())
        ->method('match')
        ->will($this->throwException($exception));

       $resolver = $this->getMock('Symfon\Component\HttpKernel\Controller\ControllerResolverInterface');

       return new Framework($matcher, $resolver);

   }

}

## Total Coverage
We want to see how great we are, so we are going to generate the coverage report:

$ phpunit --coverage-html=cov/

Now we can be proud:

Show Notes

  1. This screencast is based on the blog post written by the creator of Symfony2 Components – Fabien Potencier
  2. I strongly recommend reading the Testing chapter from the excellent book by Matt Zanndstra – PHP Objects, Patterns, and Practice – the invaluable source of information how to write hight quality and modern PHP applications.
andremaha

About The Author

No Comments

Leave A Reply