Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing in Silex (mock closures) using PHPUnit

I started working with Silex and it's just great. The problem appears when trying to proper unit test my classes. Concretely the closures :( In the following lines I explain my problem to see if any of you knows how to solve it. Please, do not focus on the syntax but the testing problem itself.

I have a provider like this:

<?php

namespace Foo\Provider;

use Silex\Application;
use Silex\ServiceProviderInterface;

use Foo\Bar;

class BarProvider implements ServiceProviderInterface {

    public function register( Application $app ) {
        $app[ 'foo_bar' ] = $app->protect( function() use ( $app ) {
            return new Bar( $app );
        } );
    }

    public function boot( Application $app ) {}
}

Then I need to get an instance of the foo_bar element:

<?php

namespace Foo;

use Silex\Application;

class Clazz {
    protected $bar;

    public function __construct( Application $app ) {
        $this->bar = $app[ 'foo_bar' ]();
    }
}

This works just fine. The thing is that I'm developing using TDD (and PHPUnit) and It's impossible for me to proper test the Clazz class.

<?php

namespace Foo\Test;

use PHPUnit_Framework_TestCase;

use Silex\Application;

use Foo\Bar;
use Foo\Clazz;

class ClazzTest extends PHPUnit_Framework_TestCase {

    public function testConstruct() {
        $mock_bar = $this->getMock( 'Foo\Bar' );

        $mock_app = $this->getMock( 'Silex\Application' );
        $mock_app
            ->expects( $this->once() )
            ->method( 'offsetGet' )
            ->with( 'foo_bar' )
            ->will( $this->returnValue( $mock_bar ) );

        new Class( $mock_app );
    }
}

The problem here resides in the "()" after the $app[ 'foo_bar' ] in the Clazz class. When trying to execute the test I get a "PHP Fatal error: Function name must be a string in ..." error. I understand I cannot unit test the class this way but I don't see the proper way to do it.

How could I test this statement (because at the end the only complex statement is $this->bar = $app 'foo_bar' ; )?

like image 875
ThisIsErico Avatar asked Oct 22 '22 06:10

ThisIsErico


1 Answers

Ok, I think I managed to test properly this closure! The final test looks like this:

<?php

namespace Foo\Test;

use PHPUnit_Framework_TestCase;

use Silex\Application;

use Foo\Bar;
use Foo\Clazz;

class ClazzTest extends PHPUnit_Framework_TestCase {

    public function testConstruct() {
        $mock_bar = $this->getMock( 'Foo\Bar' );

        $mock_app = $this->getMock( 'Silex\Application' );
        $mock_app
            ->expects( $this->once() )
            ->method( 'offsetGet' )
            ->with( 'foo_bar' )
            ->will( $this->returnValue( function() use( $mock_bar ) { return $mock_bar; } ) );

        new Class( $mock_app );
    }
}

Instead of returning the mock, I return a closure that returns the mock. This way I don't get the error while still working with the actual mock.

Can anyone tell me if this is a correct approach?

like image 151
ThisIsErico Avatar answered Oct 27 '22 11:10

ThisIsErico