Search code examples
phpunitaclsymfonyphpspec

Symfony unit test Security (ACL - Annotation)


I'd like to check a method with access control, e.g. a method is only granted with a specific role. Therefore, I know two ways in Symfony:

  1. @Security annotation above the method (SensioFrameworkExtraBundle) OR
  2. Calling authorization_checker explizit within my method

When it comes to unit tests (for my case phpspec, but I think phpunit behaviour is the almost the same in that case), I would like to test that only anonymous users should be able to call a method. With number 2. , it's working fine. Here my setup:

RegistrationHandlerSpec:

class RegistrationHandlerSpec extends ObjectBehavior
{     
   function let(Container $container, AuthorizationCheckerInterface $auth) {
     $container->get('security.authorization_checker')->willReturn($auth);
     $this->setContainer($container);
   }

   function it_should_block_authenticated_users(AuthorizationCheckerInterface $auth)
   {
     $auth->isGranted("ROLE_USER")->willReturn(true);
     $this->shouldThrow('Symfony\Component\Security\Core\Exception\AccessDeniedException')->during('process', array());
   }  
}

And within the RegistrationHandler, I have the following method:

class RegistrationHandler
{
  public function process()
  {
     $authorizationChecker = $this->get('security.authorization_checker');
     if ($authorizationChecker->isGranted('ROLE_USER')) {
         throw new AccessDeniedException();
     }
     // ...
  }
}

Well, this approach is working fine - BUT normally, I would prefer using 1. with Security annotation (Sensio FrameworkExtraBundle), and therefore, it's not working / I don't know why no Exception gets triggered when it's written as an annotation:

/**
 * @Security("!has_role('ROLE_USER')")
 */
public function process()
{
   // ...
}

Does anyone know how to get this example to work by using the first approach with @Security annotation, which is way more readable and best practice recommended of symfony?


Solution

  • In both cases you're testing a behaviour that's provided by third party code (the Symfony framework). Following the rule don't mock what you don't own, rather than writing a unit test you should write an integration test. Otherwise you'll be only making assumptions on how the code works with no proof it really works this way.

    In your case your integration test could be a controller test. You'd call the URL with a web test client (provided by the WebTestCase) and verify that in certain conditions you're getting a 401 or 403 response.

    PHPSpec is a unit testing tool (a.k.a. a design tool). You need to write integration tests with something else (for example PHPUnit). I usually have at least three testing tools installed in my project:

    • PhpSpec for unit testing
    • PHPUnit for integration testing
    • Behat for acceptance testing (a form of integration testing)