Alexandre Mélard
Alexandre Mélard

Reputation: 12649

Unit Test Symfony2

I am trying to use Mockery in order to unit test my sf2 functions. I am strugeling with my first attempt.

First try at test a class which uses security context:

public function setSecurityContext(SecurityContext $securityContext)
{
    $this->securityContext = $securityContext;
    try {
        $this->isLoggedIn = $securityContext->isGranted('IS_AUTHENTICATED_FULLY');
        $this->user = $securityContext->getToken()->getUser();
    } catch (\Exception $e) {
        $this->isLoggedIn = false;
        $this->user = $securityContext->getToken()->getUser();
    }
}

I create a testsetSecurityContext function like this:

public function testsetSecurityContext()
{
    /* @var $securityContext SecurityContext */
    $securityContext = m::mock('Symfony\Component\Security\Core\SecurityContext');

    $securityContext->shouldReceive('isGranted')
    ->with('IS_AUTHENTICATED_FULLY')
    ->once()
    ->andReturn(true);

    $factory = m::mock('Knp\Menu\FactoryInterface');

    $menu = new MenuBuilder($factory);

    $menu->setSecurityContext($securityContext);
}

When running unit test, I receive the error:

testsetSecurityContext

Mockery\Exception: The method isGranted is marked final and it is not possible to generate a mock object with such a method defined. You should instead pass an instance of this object to Mockery to create a partial mock.

So I change my test function accordingly:

public function testsetSecurityContext()
{
    /* @var $securityContext SecurityContext */
    $securityContext = m::mock(new \Symfony\Component\Security\Core\SecurityContext());
    /* ... skipped ... */
}

Now I receive that error:

testsetSecurityContext

ErrorException: Catchable Fatal Error: Argument 1 passed to Symfony\Component\Security\Core\SecurityContext::__construct() must implement interface Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface, none given, called in ..MenuBuilderTest.php on line 91 and defined in ..Symfony\Component\Security\Core\SecurityContext.php line 41

So I modify my code again:

public function testsetSecurityContext()
{

    $auth = m::mock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');

    /* @var $securityContext SecurityContext */
    $securityContext = m::mock(new \Symfony\Component\Security\Core\SecurityContext($auth));

    /* ... skipped ... */

}

And I get another error:

testsetSecurityContext

ErrorException: Catchable Fatal Error: Argument 2 passed to Symfony\Component\Security\Core\SecurityContext::__construct() must implement interface Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface, none given, called in ...\MenuBuilderTest.php on line 94 and defined in ...\Symfony\Component\Security\Core\SecurityContext.php line 41

I endup with:

public function testsetSecurityContext()
{

    $am = m::mock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface');
    $adm = m::mock('Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface');

    /* @var $securityContext SecurityContext */
    $securityContext = m::mock(new \Symfony\Component\Security\Core\SecurityContext($am, $adm));

    $securityContext->shouldReceive('isGranted')
    ->with('IS_AUTHENTICATED_FULLY')
    ->once()
    ->andReturn(true);

    $factory = m::mock('Knp\Menu\FactoryInterface');

    $menu = new MenuBuilder($factory);

    $menu->setSecurityContext($securityContext);
}

And that is still not OK as I get that error:

testsetSecurityContext

ErrorException: Catchable Fatal Error: Argument 1 passed to Atos\Worldline\Fm\Integration\Ucs\EventFlowAnalyser\Menu\MenuBuilder::setSecurityContext() must be an instance of Symfony\Component\Security\Core\SecurityContext, instance of Mockery_50c5c1e0e68d2 given, called in ..\MenuBuilderTest.php on line 106 and defined in ..\MenuBuilder.php line 140

I really would appreciate some help before I end-up with 100 lines test to test a 8 lines function...

Upvotes: 1

Views: 2579

Answers (1)

Louis-Philippe Huberdeau
Louis-Philippe Huberdeau

Reputation: 5431

Instead of mocking the instance, go for the interface it implements. It almost always works better and nearly everything in Symfony2 has well defined interfaces.

If MenuBuilder is a custom class, it should use the interface too rather than the actual implementation.

Symfony\Component\Security\Core\SecurityContextInterface

public function testsetSecurityContext()
{
    /* @var $securityContext SecurityContext */
    $securityContext = m::mock('Symfony\Component\Security\Core\SecurityContextInterface');

    $securityContext->shouldReceive('isGranted')
    ->with('IS_AUTHENTICATED_FULLY')
    ->once()
    ->andReturn(true);

    $factory = m::mock('Knp\Menu\FactoryInterface');

    $menu = new MenuBuilder($factory);

    $menu->setSecurityContext($securityContext);
}

Upvotes: 5

Related Questions