4

I am writing a functional test for a page that requires user authentication. I am using the sfDoctrineGuard plugin.

How do I authenticate a user in my test?

Do I have to enter every test through the sign in screen?

Here is my incorrect code:

$b->post('/sfGuardAuth/signin',
       array('signin[password]' => 'password',
             'signin[username]' => 'user',
             'signin[_csrf_token]' => '7bd809388ed8bf763fc5fccc255d042e'))->
       with('response')->begin()->
         checkElement('h2', 'Welcome Humans')->
       end()

Thank you

2 Answers 2

6

The tricky part about doing a signin is that the test browser wipes out the context object before each request (see sfBrowser::call()).

You can authenticate the user by injecting a listener which will call the user's signIn() method when the context.load_factories event fires during context initialization:

function signin( sfEvent $event )
{
  /* @var $user sfGuardSecurityUser */
  if( ! $user = $event->getSubject()->getUser() )
  {
    throw new RuntimeException('User object not created.');
  }

  if( ! $user instanceof sfGuardSecurityUser )
  {
    throw new LogicException(sprintf(
      'Cannot log in %s; sfGuardSecurityUser expected.',
        get_class($user)
    ));
  }

  if( $user->isAuthenticated() )
  {
    $user->signOut();
  }

  /* Magic happens here: */
  $user->signIn($desired_user_to_log_in_as);

  $event->getSubject()->getEventDispatcher()->notify(new sfEvent(
    $this,
    'application.log',
    array(sprintf('User is logged in as "%s".', $user->getUsername()))
  ));
}

/* Set signin() to fire when the browser inits the context for subsequent
 *  requests.
 */
$b->addListener('context.load_factories', 'signin');

This will cause the browser to sign in the user for all subsequent requests. Note that sfBrowser does not have a removeListener() method.

Adapted from sfJwtPhpUnitPlugin (FD: I'm the lead dev for this project).

Sign up to request clarification or add additional context in comments.

2 Comments

Amazing! Just want to say, when signin method defined in some child class of sfTestFunctional, a listener attachment must be called like this $b->addListener('context.load_factories', array($b, 'signin'));
I needed to force signin later in the symfony bootstrap process because my app's myUser depends on the route. So I used $b->addListener('controller.change_action', 'signin') instead.
3

Yes, you do have to sign in to carry out tests. Fortunately, this is much simpler than the method you illustrate above. See the "better and simpler way" on this blog post.

You could make the signin method part of any TestFunctional class according to how you've structured your tests.

1 Comment

the page doesn't exists anymor, but here is another way of doing [tests for loggin] (snipplr.com/view/27686)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.