如何将Behat与Mink和WebApiContext一起使用?

时间:2014-07-17 08:34:36

标签: api symfony behat mink

我正在进行的项目在登录后有一个api。

我使用behat和mink登录:

Scenario: Login
  Given I am on "/login/"
  And I should see "Login"
  When I fill in "_username" with "test"
  And I fill in "_password" with "test"
  And I press "_submit"
  Then I should be on "/"

这有效..

但是,只要我想使用WebApiContext执行以下操作,就不会存储登录会话:

Scenario: Getting the list of pages
  When I send a GET request to "/api/pages.json"
  Then print response

我在同一个功能中使用这两种方案。我的FeatureContext类看起来像这样:

class FeatureContext extends MinkContext
{
    public function __construct(array $parameters)
    {
        $context = new WebApiContext($parameters['base_url']);
        $context->getBrowser()->getClient()->setCookieJar(new \Buzz\Util\CookieJar());
        $this->useContext('web', $context);
    }
}

我从this issue添加了cookiejar的想法没有成功。当我打印响应时,我只是从登录界面看到HTML页面..

有没有人知道我是否完全走错了方向,或者我是否朝着正确的方向前进?

1 个答案:

答案 0 :(得分:2)

我成功使用了相同的方法。我认为没有一种标准的方法可以做到这一点。就您了解cookie基础知识而言,您应该能够实施该解决方案。

在常见情况下,客户端使用某些凭据向服务器发送身份验证请求,服务器验证它,启动经过身份验证的会话并发回具有该会话ID的cookie。以下所有请求都包含该id,因此服务器可以识别被调用者。可以使用特定的头而不是cookie,或者可以使用数据库代替会话,但原理是相同的,您可以(相对)轻松地使用Mink模拟它。

/**
 * Start a test session, set the authenticated user and set the client's cookie.
 *
 * @Given /^I am signed in$/
 */
signIn()
{
    session_start();
    $_SESSION['user'] = 'jos';
    $this->getSession()->getDriver()->setCookie(session_name(), session_id());
    session_commit();
}

上面的步骤定义(Behat 3)是它的基础,你手动创建经过身份验证的会话并将其设置为客户端的id。这也必须是另一个例子所说明的内容。

当你开始做更复杂的事情时,PHP的会话可能会有问题,并且这个解决方案有几个大的水下岩石。如果要从两个角度(客户端和服务器)运行断言,您可能经常需要同步会话。这可以通过在所有Mink步骤之前更新cookie并在之后重新加载会话来完成。

/**
 * @beforeStep
 * @param BeforeStepScope $scope
 */
public function synchroniseClientSession(BeforeStepScope $scope)
{

    // Setup session id and Xdebug cookies to synchronise / enable both.

    $driver         = $this->getSession()->getDriver();

    // Cookie must be set for a particular domain.

    if ($driver instanceof Selenium2Driver && $driver->getCurrentUrl() === 'data:,') {
        $driver->visit($this->getMinkParameter('base_url'));
    }

    // Also enables the debugging support.

    $driver->setCookie(session_name(), session_id());
    $driver->setCookie('XDEBUG_SESSION', 'PHPSTORM');
}

/**
 * @afterStep
 * @param AfterStepScope $scope
 */
public function synchroniseServerSession(AfterStepScope $scope)
{
    $driver = $this->getSession()->getDriver();

    // Only browser kit driver, only initiated requests, only not repeating requests.

    if (!$driver instanceof BrowserKitDriver) {
        return;
    } elseif (($request = $driver->getClient()->getRequest()) === null) {
        return;
    } elseif ($request === self::$request) {
        return;
    }

    // Your logic for reloading the session.

    self::$request = $request;
}

我遇到的最大问题是会话重新加载。这可能是由于我选择的框架,我怀疑。第一个代码段有session_commit(),用于保存和关闭会话。理论上,在以下步骤定义中,您必须能够session_id(/* session id from the cookie… */);session_start();,但实际上这不起作用,并且实际上没有从文件加载会话数据,尽管会话确实已开始。为了解决这个问题,我使用session save handler创建了一个使用reload()方法的自定义会话管理器。

第二个问题是你不能简单地关闭会话而不是编写或破坏会话(支持在PHP 5.6中添加),它依赖于重新加载本身。我用会话管理器的标志重新发明了轮子,告诉它是写还是只是关闭它。

:)