0

The project i'm working on has a api behind a login.

I'm using behat with mink to login:

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 "/"

This works..

However, the login session is not stored whenever i want to do the following using the WebApiContext:

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

I'm using both scenarios in the same feature. My FeatureContext class looks something like this:

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);
    }
}

I added the cookiejar idea from this issue without success.. When i print the response i just see the HTML page from the login screen..

Does anyone have any idea if i'm going at this totally the wrong way or am i somewhat in the right direction?

Jos Gerrits
  • 245
  • 2
  • 11

1 Answers1

2

I am successfully using the same method. I don't think there's a standard way of doing this. As far as you understand the cookie basics you should be able to implement the solution.

In a common scenario, a client sends an authentication request to the server with some credentials, the servers validates it, starts an authenticated session and sends back a cookie with that session id. All following requests contain that id, so the server can recognise the callee. A specific header can be used instead of the cookie, or a database can be used instead of the session, but the principle is the same and you can (relatively) easily simulate it with 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();
}

The above step definition (Behat 3) is the basics of it, you manually create the authenticated session and set to the client it's id. That must be also what the other example illustrates.

PHP's sessions can be problematic when you start doing more complex things and there are a couple of big underwater rocks with this solution. If you want to run assertions from both perspectives (the client and the server) you might often need to have your sessions synced. This can be done by updating the cookie before all Mink steps and reloading the session after.

/**
 * @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;
}

The biggest problem I had was the session reloading. This might be due to my framework of choice, which I doubt. The very first code snippet has session_commit(), which saves and closes the session. In theory in the following step definitions you must be able to session_id(/* session id from the cookie… */); and session_start();, but in practice that didn't work and no session data was actually loaded from the file, though the session did start. To solve this I created a custom session manager with reload() method using session save handler.

Second problem is where you cannot simply close the session without either writing it or destroying it (the support is added in PHP 5.6) on which relies the reloading itself. I reinvented the wheel with a flag for the session manager which tells it whether to write or just to close it.

:)

Ian Bytchek
  • 8,804
  • 6
  • 46
  • 72