Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHPUnit, Interfaces and Namespaces (Symfony2)

I'm currently working on an open source bundle for Symfony2, and really want it to be the dogs nadgers in terms of unit test coverage and general reliability, however I've run into a snag due to my lack of PHPUnit knowledge (or a complex scenario, who knows)..

At present, I have a Mailer class, for handling individual mail scenarios. It looks a bit like this:

<?php
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
use Symfony\Component\Routing\RouterInterface;

class Mailer
{
    protected $mailer;
    protected $router;
    protected $templating;
    protected $parameters;

    public function __construct($mailer, RouterInterface $router, EngineInterface $templating, array $parameters)
    {
        $this->mailer = $mailer;
        $this->router = $router;
        $this->templating = $templating;
        $this->parameters = $parameters;
    }
}

Simple enough, got some Symfony2 interface gubbins in there to handle different routing and templating systems, happy happy joy joy.

Here's the initial test I tried setting up for the above:

<?php
use My\Bundle\Mailer\Mailer

class MailerTest extends \PHPUnit_Framework_TestCase
{
    public function testConstructMailer
    {
        $systemMailer = $this->getSystemMailer();
        $router = $this->getRouter();
        $templatingEngine = $this->getTemplatingEngine();

        $mailer = new Mailer($systemMailer, $router, $templatingEngine, array());
    }

    protected function getSystemMailer()
    {
        $this->getMock('SystemMailer', array('send');
    }   
    protected function getRouter()
    {
        $this->getMock('RouterInterface', array('generate');
    }

    protected function getTemplatingEngine()
    {
        $this->getMock('RouterInterface', array('render');
    }
}

The problem here is that my mock objects do not implement Symfony\Bundle\FrameworkBundle\Templating\EngineInterface and Symfony\Component\Routing\RouterInterface, so I can't use any mock objects that I create myself. One method I have tried is creating an abstract class which implements the correct interface on the test page, however the getMockForAbstractClass fails, stating it can't find the class...

like image 529
domudall Avatar asked Nov 16 '11 15:11

domudall


1 Answers

When mocking you need to use the full qualified class path as the mock functionality is not taking the namespace of the calling code or any "use" statements into consideration.

Try

->getMock('\\Symfony\\Component\\Routing\\RouterInterface'); 

and leave out the second parameter. Usually specifying the methods does a lot more worse than good. Only if you want all the other methods to work like before than you should need the second parameter.

Example

<?php

namespace bar;

class MyClass {}

namespace foo;

use \bar\MyClass;

class MockingTest extends \PHPUnit_Framework_TestCase {

    public function testMock() {
        var_dump($this->getMock('MyClass') instanceOf MyClass);
        var_dump($this->getMock('\\bar\\MyClass') instanceOf MyClass);
    }   
}

Produces:

/phpunit.sh --debug fiddleTestThree.php 
PHPUnit @package_version@ by Sebastian Bergmann.


Starting test 'foo\MockingTest::testMock'.
.bool(false)
bool(true)
like image 107
edorian Avatar answered Oct 13 '22 17:10

edorian