Codeception: Keep a logged in state

I think that the accepted answer is a way of accomplish it, but not the best. Doing that you'll always have to reproduce all the steps to log in into the system, when you only need to share the user session. I think that grabbing the session cookie and pass it through the required user logged tests is better. To do that, you create a login function, get the cookie, and make your tests depends on the login test first:

<?php
use \AcceptanceTester;

class testingCest
{
    private $cookie = null;

    public function _before(AcceptanceTester $I)
    {
    }

    public function _after(AcceptanceTester $I)
    {
    }


    // tests
    public function login(AcceptanceTester $I)
    {
        $I->wantTo('entrar al sistema');
        $I->amOnPage('/');
        $I->seeInCurrentUrl('/admin/login');
        $I->fillField('user','perry');
        $I->fillField('pass','pass-perry');
        $I->click('Login');
        $I->see('You\'re logged!');
        $this->cookie   = $I->grabCookie('your-session-cookie-name');
    }

    /**
     * @depends login
     */
    public function listUsers(AcceptanceTester $I)
    {
        $I->setCookie( 'your-session-cookie-name', $this->cookie );
        $I->amOnPage('/admin/users');
        $I->seeInCurrentUrl('/admin/users/1');
    }

    /**
     * @depends login
     */
    public function listRols(AcceptanceTester $I)
    {
        $I->setCookie( 'your-session-cookie-name', $this->cookie );
        $I->amOnPage('/admin/rols');
        $I->seeInCurrentUrl('/admin/rols/1');
    }
}

That way, if the login test fails, you won't get the cookie, and you won't pass the other tests.

I prefer this @depends annotation instead of the @before proposed in the other answer, because if you use @depends you'll ALWAYS execute the code in the test before, and the tests will be only executed after the login.

UPDATE

There exists another answer bellow, https://stackoverflow.com/a/41109855/1168804 that may also help you, as the framework for Codeception has evolved since the writing of this answer.


All earlier answers are old, now it's done directly in _before method which takes Actor class as argument.

<?php

namespace Test\Api;

use ApiTester;

class TrainingCest
{
    public function _before(ApiTester $I)
    {
        $I->amLoggedInAs('kgkg');
    }

    public function _after(ApiTester $I)
    {
    }

    // tests
    public function testForLoggedInUser(ApiTester $I)
    {
    }

    public function anotherTestForLoggedInUser(ApiTester $I)
    {
    }

}

And if you want to log in just once for all CEST files, you can use global Registry class implementing Registry design pattern (see https://dzone.com/articles/practical-php-patterns/basic/practical-php-patterns-0) along with some lazyloading. Here is working code for my api integration tests defined in Actor class (in my case ApiTester):

public function amLoggedInAs($userLogin)
{
    $I = $this;

    if (Registry::getInstance()->exists($userLogin)) {
        // get data from registry
        $storedUserData = Registry::getInstance()->get($userLogin);
        $newAccessToken = $storedUserData['accessToken'];
        $playerId = $storedUserData['playerId'];
    }
    else {
        // no registry data - log in and save data in registry
        $I->tryToLogin($userLogin);

        $newAccessToken = $I->grabDataFromResponseByJsonPath('data.newToken');
        $playerId = (int)$I->grabDataFromResponseByJsonPath('data.userId');
        Registry::getInstance()->set($userLogin, [
            'accessToken' => $newAccessToken,
            'playerId' => $playerId
        ]);
    }

    // finally set headers and some other data
    $I->haveHttpHeader('X-Token', $newAccessToken);
    $I->havePlayerId($playerId);
}

protected function tryToLogin($userLogin)
{
    $I = $this;

    $I->wantTo('login into api');
    $I->amGoingTo('try to log to API using login and password');
    $I->sendPOST('/system/login', ['login' => $userLogin, 'password' => self::getPassword($userLogin)]);

    // ...some other checking if user was correctly logged in ...
}

This code basically stores accessToken along with some additional data in Registry after user logged for first time. If another call to $I->amLoggedInAs('kgkg') is invoked, it gets these values from registry. You can have many logged users this way, each of them logged only once per suite.

You can use another method for autorization instead of custom token, logic will still be the same.

Also if you're using WebDriver (not PhpBrowser), you can use loadSessionSnapshot and saveSessionSnapshot instead of Registry to get quite the same result.