Test your Symfony Controller and your service with PhpUnit

21 May 2015 - Tags: php

In this article I would like share with you a little experience with:

  • Symfony MVC
  • PhpUnit
  • Symfony Dependence Injaction

This is an example of very easy controller.

<?php
namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;

class SomeStuffController extends FOSRestController
{
    /**
     * @Rest\Post("/go")
     * @return array
     */
    public function goAction(Request $request)
    {
        if($this->container->getParameter("do_stuff")) {
            $body = $this->container->get("stuff.service")->splash($request->getContent());
        }
        return [];
    }
}

$this->container->getParameter("do_stuff") is a boolean parameter that enable or disable a feature, How can I test this snippet? I can try to write a functional test but in my opinion is easier write a series of unit tests with PhpUnit to validate my expectations.

Expectations

  • If do_stuff parameter is false function get by my container will be call zero times
  • If do_stuff parameter is true function get by my container will be call one times
<?php

namespace AppBundle\Tests\Controller;

use Liip\FunctionalTestBundle\Test\WebTestCase;
use AppBundle\Controller\SomeStuffController;

class SomeStuffControllerTest extends WebTestCase
{
    public function testDoStuffIsTrue()
    {
        $request = $this->getMock("Symfony\Component\HttpFoundation\Request");
        $container = $this->getMock("Symfony\Component\DependencyInjection\ContainerInterface");
        $service = $this->getMockBuilder("Some\Stuff")->disableOriginalConstructor()->getMock();
        $container->expects($this->once())
            ->method("getParameter")
            ->with($this->equalTo('do_stuff'))
            ->will($this->returnValue(true));

        $container->expects($this->once())
            ->method("get")
            ->with($this->equalTo('stuff.service'))
            ->will($this->returnValue($service));

        $controller = new SameStuffController();
        $controller->setContainer($container);

        $controller->goAction($request);

    }
}

This is my first expetection “If do_stuff param is true I call stuff.service”. In this controller I use a few objects, Http\Request, Container and stuff.service in this example is a Some\Stuff class. In the first step I have created one mock for each object.

<?php
$request = $this->getMock("Symfony\Component\HttpFoundation\Request");
$container = $this->getMock("Symfony\Component\DependencyInjection\ContainerInterface");
$service = $this->getMockBuilder("Some\Stuff")->disableOriginalConstructor()->getMock();

In the second step I have written my first expetctation, “Call only one time function getParameter from $container with argument do_stuff and it returns true”.

<?php
$container->expects($this->once())
    ->method("getParameter")
    ->with($this->equalTo('do_stuff'))
    ->will($this->returnValue(true));

Thanks at this definitions I know that there will be another effect, my action will call only one time $container->get("stuff.service") and it will be return an Some\Stuff object.

The second test that we can write is “if do_stuff is false $contaner->get("stuff.service") it will not be called.

<?php
public function testDoStuffIsFalse()
{
    $request = $this->getMock("Symfony\Component\HttpFoundation\Request");
    $container = $this->getMock("Symfony\Component\DependencyInjection\ContainerInterface");
    $service = $this->getMockBuilder("Some\Stuff")->disableOriginalConstructor()->getMock();
    $container->expects($this->once())
        ->method("getParameter")
        ->with($this->equalTo('do_stuff'))
        ->will($this->returnValue(false));

    $container->expects($this->never())
        ->method("get")
        ->with($this->equalTo('stuff.service'))
        ->will($this->returnValue($service));

    $controller = new SameStuffController();
    $controller->setContainer($container);
    $controller->goAction($request);
}