23

我想在大多数测试之前保留或运行登录。但是,如果我尝试将登录代码移动到 _before 它不起作用,因为我没有可用的 webguy 实例。

在多个测试之间保持会话的最佳方法是什么?到目前为止,这是我的代码,很高兴得到一些帮助。我已经用谷歌搜索并检查了文档,但我找不到任何关于会话的东西。

<?php
use \WebGuy;

class ProductCest
{

    private $product_id = '1';

    public function _before()
    {
    }

    public function _after()
    {
    }

    // tests
    public function login(WebGuy $I) {
        $I->seeInCurrentUrl('/auth/login');
        $I->fillField("//input[@type='email']", "username@email.com");
        $I->fillField("//input[@type='password']", "1234");
        $I->click('#signIn .submit');
        $I->wait(500);

        $I->seeInCurrentUrl('/account');
    }

    /**
     * @depends login
     */
    public function chooseProduct(WebGuy $I) {
        $I->wantTo('go to products and choose one');
        $I->amOnPage('/?product=' . $this->client_id);
    }

}
4

8 回答 8

22

我认为公认的答案是实现它的一种方式,但不是最好的。这样做,当您只需要共享用户会话时,您将始终必须重现登录系统的所有步骤。我认为获取会话 cookie 并将其通过所需的用户记录测试会更好。为此,您创建一个登录函数,获取 cookie,并使您的测试首先依赖于登录测试:

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

这样,如果登录测试失败,您将无法获取 cookie,并且您将无法通过其他测试。

我更喜欢这个@depends注释而不是@before其他答案中的建议,因为如果你使用@depends,你总是会在之前执行测试中的代码,并且测试只会在登录后执行。

更新

下面还有另一个答案,https ://stackoverflow.com/a/41109855/1168804,它也可能对您有所帮助,因为自编写此答案以来,Codeception 的框架已经发展。

于 2014-08-28T11:03:59.767 回答
6

所有早期的答案都是旧的,现在它直接在 _before 方法中完成,该方法以 Actor 类作为参数。

<?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)
    {
    }

}

如果您只想为所有 CEST 文件登录一次,您可以使用实现 Registry 设计模式的全局 Registry 类(参见https://dzone.com/articles/practical-php-patterns/basic/practical-php-patterns- 0 ) 以及一些延迟加载。这是我在 Actor 类中定义的 api 集成测试的工作代码(在我的例子中是 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 ...
}

此代码基本上在用户第一次登录后将 accessToken 以及一些附加数据存储在注册表中。如果调用 $I->amLoggedInAs('kgkg') 的另一个调用,它会从注册表中获取这些值。通过这种方式,您可以拥有许多登录用户,每个用户在每个套件中只登录一次。

您可以使用其他方法进行自动化而不是自定义令牌,逻辑仍然相同。

此外,如果您使用的是 WebDriver(而不是 PhpBrowser),则可以使用 loadSessionSnapshot 和 saveSessionSnapshot 而不是 Registry 来获得完全相同的结果。

于 2016-12-12T21:25:58.553 回答
4

由于saveSessionSnapshot和 loadSessionSnapshot 方法,现在 Codeception 可以解决这个问题。

<?php
// inside AcceptanceTester class:

public function login()
{
     // if snapshot exists - skipping login
     if ($I->loadSessionSnapshot('login')) return;

     // logging in
     $I->amOnPage('/login');
     $I->fillField('name', 'jon');
     $I->fillField('password', '123345');
     $I->click('Login');

     // saving snapshot
     $I->saveSessionSnapshot('login');
}
?>

然后在您的测试课程中,您只需这样做

public function _before(AcceptanceTester $I)
{
    $I->login();
}
于 2019-04-09T15:43:36.030 回答
2
<?php
use \WebGuy;

class ProductCest
{

    private $product_id = '1';

    public function _before()
    {
    }

    public function _after()
    {
    }

    private function executeLogin(WebGuy $I){
        $I->seeInCurrentUrl('/auth/login');
        $I->fillField("//input[@type='email']", "username@email.com");
        $I->fillField("//input[@type='password']", "1234");
        $I->click('#signIn .submit');
        $I->wait(500);
        return $I;
    }

    // tests
    public function login(WebGuy $I) {
        $I = $this->executeLogin($I);

        $I->seeInCurrentUrl('/account');
    }

    /**
     * @depends login
     */
    public function chooseProduct(WebGuy $I) {
        $I = $this->executeLogin($I);
        $I->wantTo('go to products and choose one');
        $I->amOnPage('/?product=' . $this->client_id);
    }
}

这应该有效。

于 2014-01-04T01:17:01.290 回答
1

我认为@Sinisa 的答案是“有效”的,但不是“正确的”。

你想要的不是@depends 注释,而是@before。

不同之处在于@depends 不保留当前上下文,而使用@before 保留上下文。

public function foo(WebGuy $I)
{
    $I->amOnPage('/foobar');
}

/**
 * @depends foo
 */
public function bar(WebGuy $I)
{
    $I->seeInCurrentUrl('foobar'); // Wil fail
}

/**
 * @before foo
 */
public function baz(WebGuy $I)
{
    $I->seeInCurrentUrl('foobar'); // Will pass
}
于 2014-06-28T13:43:46.703 回答
1

请记住,如果您使用 SaveSessionSnapshot,则必须确保在您的登录完全完成后调用该函数。这意味着如果您的登录需要一些时间(ldap-login 或安全检查),SaveSessionSnapshot 会在登录完成之前执行并保存一个空会话。如果是这样,您必须告诉您的程序等待($I->wait()),或者检查登录是否完全完成。

于 2021-03-18T12:36:08.343 回答
1

在我的情况下,在 2021 年,它就像在 .yml 文件中设置clear_cookiesfalse一样简单。

Codeception 会在测试之间删除 cookie,因为您应该在测试之间保持登录状态作为最佳实践。

但是,如果您想保留 cookie,只需声明以下内容:

actor: AcceptanceTester
modules:
    enabled:
        - WebDriver:
              clear_cookies: false
于 2021-06-04T06:19:59.620 回答
1

需要注意的是,如果您正在测试 WordPress,则 WP-browser 模块具有用于登录的“糖”方法:

loginAsAdmin();
loginAs($username, $password);

https://github.com/lucatume/wp-browser

于 2017-10-27T22:19:54.373 回答