3

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?

user3746259
  • 1,491
  • 2
  • 23
  • 46

1 Answers1

6

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)
Jakub Zalas
  • 35,761
  • 9
  • 93
  • 125
  • Hi Jakub, thanks for your answer, now I understand a lot more on testing. So I would use the following order to develop a new feature with the tools you described: 1) BDD lifecycle (Behat, PHPSpec, Prodcode, Refactor) for "normal" classes and 2) When it comes to creating a controller, I would first create a very small phpspec and a bigger integration test, because Controllers are not unit tested much but functional? In addition, what other things do you test? Answering these two questions should fully clear my mind and will result in a bounty. Regards – user3746259 Feb 26 '16 at 15:13
  • 1
    Depends if you like to build from "outside-in" or "inside-out". Most of people prefer outside-in. You start with some kind of an acceptance test (Behat or PHPUnit), or a functional test (PHPUnit). As you go deeper you'll write unit tests for classes (PhpSpec or PHPUnit), decoupling from infrastructure and introducing interfaces to mock them. What's left is testing implementations of those infrastructure relying interfaces. That's where integration tests come in. You can also replace infrastructure infrastructure implementations in your acceptance once with simpler ones to make them faster. – Jakub Zalas Feb 28 '16 at 15:57
  • As for testing controllers - depends how you treat them, but most of the time yes, you'd cover them with some kind of integration tests (like functional tests). – Jakub Zalas Feb 28 '16 at 16:16
  • Do you have a good Link for using interfaces? I know what they do but I use them very rare. Thanks for your thoughts! – user3746259 Feb 28 '16 at 16:41
  • No link comes to my mind. It's pure OOD. This book perhaps: http://www.amazon.com/Agile-Principles-Patterns-Practices-C/dp/0131857258 – Jakub Zalas Feb 28 '16 at 21:13