Test your Symfony Controller and your service with PhpUnit

21 May 2015 · Two minute read · on Gianluca's blog

News! Tech related notes are NOW published to ShippingBytes. See you there! I always felt this was not the right place for me to write consistently about tech and tools. So if you want to read more about that see you at the other side

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

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

<?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);
}
Something weird with this website? Let me know.