【问题标题】:Behat authenticate Symfony2 userBehat 验证 Symfony2 用户
【发布时间】:2011-12-27 19:46:53
【问题描述】:

我在 Symfony2 / Doctrine2 中使用 Behat。现在,我有这样一个场景,归结为“如果我登录并转到 /login,我应该转到 / 而不是”:

@login
Scenario: Go to the login page while being logged in
  Given I am logged in
  When I go to "/login"
  Then I should be on "/"

对于@login,我创建了以下内容:

/**
 * @BeforeScenario @login
 */
public function loginUser()
{
    $doctrine = $this->getContainer()->get('doctrine');
    $userRepository = $doctrine->getRepository('MyTestBundle:User');
    $user = $userRepository->find(1); // 1 = id

    $token = new UsernamePasswordToken($user, NULL, 'main', $user->getRoles());
    $this->getContainer()->get('security.context')->setToken($token);
}

在“当我转到 /login”代码(控制器被调用)中,令牌似乎消失了(不是我想要的):

/**
 * @Route("/login", name="login")
 */
public function loginAction()
{
    $token = $this->get('security.context')->getToken();
    $fd = fopen('/tmp/debug.log', 'a');
    fwrite($fd, $token);

    // prints 'AnonymousToken(user="anon.", authenticated=true, roles="")'
    ...

但在 FeatureContext 中,它似乎一直存在(我希望它会起作用)。在“鉴于我已登录”中:

/**
 * @Given /^I am logged in$/
 */
public function iAmLoggedIn()
{        
    $token = $this->getContainer()->get('security.context')->getToken();
    $fd = fopen('/tmp/debug.log', 'a');
    fwrite($fd, $token);

    // prints 'UsernamePasswordToken(user="admin", authenticated=true, roles="ROLE_ADMIN")'
    ...

我是这样跑的:

app/console -e=test behat

我也在控制器中这样做以确保它是测试:

fwrite($fd, $this->get('kernel')->getEnvironment());
// prints 'test'

任何线索如何验证用户?我将不得不测试很多管理页面,所以如果我可以将登录信息挂接到@BeforeSuite、@BeforeFeature(或@BeforeScenario ...),这样我就不会被阻止。

(也欢迎有关禁用身份验证机制以进行测试的建议,或存根/模拟用户的方法。)

【问题讨论】:

    标签: symfony behat


    【解决方案1】:

    哦,天哪。它不起作用,因为您的 FeatureContext 中的 DIC 没有与您的应用程序共享 - 您的应用程序具有单独的内核和 DIC。您可以通过 Mink 获得它。或者,您可以简单地以正确的方式进行:-)

    正确的方式意味着,最终用户可观察到的行为的每个部分都应该在 *.feature 内部进行描述,而不是在 FeatureContext 内部进行描述。这意味着,如果你想登录一个用户,你应该简单地用步骤来描述它(比如:“我在 /login”,“我填写用户名......”,“我填写密码”和 stuf) .如果您想多次执行此操作 - 您应该创建一个元步骤。

    元步骤只是描述多个其他步骤的简单步骤,例如 - “我以 everzet 身份登录”。你可以在这里阅读它们:http://docs.behat.org/guides/2.definitions.html#step-execution-chaining

    【讨论】:

    • 确实好多了(而且更简单)。只是将更改推送到我的项目中。谢谢!
    • 如果谈及oAuth授权怎么办?我只需要在功能上下文中对用户进行身份验证,只需将其设置为登录而无需任何其他步骤,因为在这种情况下它没有帮助
    • 你提到可以得到应用的内核和DIC,你会怎么做?
    • 仅仅因为用户必须使用登录页面才能访问某个区域,并不意味着所有测试也应该使用登录表单。通过这样做,您现在将每个测试场景耦合到登录表单。在许多应用程序中,还有“记住我”功能和电子邮件链接登录(电子邮件验证、忘记密码等)。仅仅因为它被称为行为驱动开发并不意味着最终用户行为。这意味着来自所有角色、用户、业务组等的预期系统行为。
    【解决方案2】:

    这是我使用过的 OAuth 登录解决方案。经过多次搜索答案并登陆此页面后,我认为分享解决方案会很棒。希望它会对某人有所帮助。

    背景:使用 HWIOAuthBundle 的 Symfony2 应用程序,连接到一些 OAuth2 提供程序。

    问题:当 Behat 上下文未与 Symfony 上下文共享时,我如何实现 Given I'm logged in

    解决方案

    HWIOAuthBundle 使用 @buzz 服务对 OAuth 提供者的所有 API 调用。因此,您需要做的就是将 Buzz 客户端替换为您的实现,该实现不调用外部服务,而是立即返回结果。这是我的实现:

    <?php
    
    namespace Acme\ExampleBundle\Mocks;
    
    use Buzz\Client\ClientInterface;
    use Buzz\Message\MessageInterface;
    use Buzz\Message\RequestInterface;
    
    class HttpClientMock implements ClientInterface
    {
        public function setVerifyPeer()
        {
            return $this;
        }
    
        public function setTimeout()
        {
            return $this;
        }
    
        public function setMaxRedirects()
        {
            return $this;
        }
    
        public function setIgnoreErrors()
        {
            return $this;
        }
    
        public function send(RequestInterface $request, MessageInterface $response)
        {
            if(preg_match('/\/oauth2\/token/', $request->getResource()))
            {
                $response->setContent(json_encode([
                    'access_token' => 'valid',
                    'token_type' => 'bearer',
                    'expires_in' => 3600
                ]));
            }
            elseif(preg_match('/\/oauth2\/me/', $request->getResource()))
            {
                $response->setContent(json_encode([
                    'id' => 1,
                    'username' => 'doctor',
                    'realname' => 'Doctor Who'
                ]));
            }
            else throw new \Exception('This Mock object doesn\'t support this resource');
        }
    }
    

    下一步是劫持 HWIOAuthBundle/Buzz 使用的类并将其替换为上面的实现。我们只需要在测试环境下这样做。

    # app/config/config_test.yml
    imports:
        - { resource: config_dev.yml }
    
    parameters:
        buzz.client.class: Acme\ExampleBundle\Mocks\HttpClientMock
    

    最后,您需要将 require_previous_session 设置为 false 以用于测试环境 - 因此我建议将其作为参数传递。

    # app/config/security.yml
    security:
        firewalls:
            secured_area:
                oauth:
                    require_previous_session: false
    

    现在你可以像这样实现你的步骤了。

    规格:

    Feature: Access restricted resource
    
      Scenario: Access restricted resource
        Given I'm logged in
        When I go to "/secured-area"
        Then I should be on "/secured-area"
        And the response status code should be 200
    

    实施:

    <?php
    /**
     * @Given /^I\'m logged in$/
     */
    public function iMLoggedIn()
    {
        $this->getSession()->visit($this->locatePath('/login/check-yourOauthProvider?code=validCode'));
    }
    

    您传递的代码不相关,您传递的任何内容都可以,因为它没有被检查。您可以在HttpClientMock::send 方法中自定义此行为。

    【讨论】:

      【解决方案3】:

      http://robinvdvleuten.nl/blog/handle-authenticated-users-in-behat-mink/ 是一篇关于如何创建登录会话并设置 Mink 会话 cookie 以使 Mink 会话登录的简单、干净的文章。这比每次登录用户时都使用登录表单要好得多。

      【讨论】:

        【解决方案4】:

        在这里调用 UI 层“内部”的层是可以的(在 symfony 中:与模型交谈)。 对于所有的 symfony 用户,behat 建议使用带表格参数的 Given 步骤来设置记录而不是固定装置。这样您就可以在一个地方阅读所有场景并从中理解,而无需在文件之间跳转:

        鉴于有用户:
        |用户名 |密码 |电子邮件 |
        |永泽 | 123456 | everzet@knplabs.com |
        | fabpot | 22@222 | fabpot@symfony.com |

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-04-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-08-21
          • 1970-01-01
          相关资源
          最近更新 更多