Codeception,使用 pageObject 设计模式和 gherkin 编写验收测试

Posted

技术标签:

【中文标题】Codeception,使用 pageObject 设计模式和 gherkin 编写验收测试【英文标题】:Codeception, write acceptance tests with the pageObject design pattern and gherkin 【发布时间】:2019-02-07 12:04:51 【问题描述】:

我正在寻找一个带有 pageObject 设计模式和 gherkin 的简单代码示例,因为当我关注 the codeception BDD documentation 时,所有示例都写在 tests/support/AcceptanceTester.php 中。我不明白(英语能力差--)如何不将所有代码集中在 AcceptanceTester.php 文件中。

例如,我有一个带有两个按钮 A 和 B 的示例主页。如果用户单击按钮 A,则加载页面 A,否则如果用户单击按钮 B,则加载页面 B。

目前,我的 AcceptanceTester

<?php
// tests/_support/AcceptanceTester.php
/**
 * Inherited Methods
 * @method void wantToTest($text)
 * @method void wantTo($text)
 * @method void execute($callable)
 * @method void expectTo($prediction)
 * @method void expect($prediction)
 * @method void amGoingTo($argumentation)
 * @method void am($role)
 * @method void lookForwardTo($achieveValue)
 * @method void comment($description)
 * @method \Codeception\Lib\Friend haveFriend($name, $actorClass = NULL)
 *
 * @SuppressWarnings(PHPMD)
 */

class AcceptanceTester extends \Codeception\Actor

    use _generated\AcceptanceTesterActions;

    /**
     * @Given The home page
     */
    public function inHomePage()
    
        $this->amOnPage("/");
        $this->seeInTitle('home');
    

    /**
     * @When I click on the button A
     */
    public function goToThePageA()
    
        $this->click(['name' => 'A']);
    

    /**
     * @Then l go to the page A
     */
    public function ImInPageA()
    
        $this->seeInTitle('page A');
    

    /**
     * @When I click on the button B
     */
    public function goToThePageB()
    
        $this->click(['name' => 'B']);
    

    /**
     * @Then l go to the page B
     */
    public function ImInPageB()
    
        $this->seeInTitle('page B');
    

如果我运行命令“./vendor/bin/codecept run accept”,一切都会像魅力一样。但正如我之前所说,我需要学习如何不将所有代码集中在 AcceptanceTester 文件中。

所以,我创建了三个 pageObjects ;一个用于主页,一个用于页面 A,一个用于页面 B。代码:

主页对象

<?php
// tests/_support/Page/PageHome.php
namespace Page;

class PageHome

    public static $URL = '/home';
    public static $title = "home";
    public static $aButton = ['name' => 'A'] ;
    public static $bButton = ['name' => 'B'] ;

    public static function route($param)
        return static::$URL.$param;
    

    /**
     * @var \AcceptanceTester;
     */
    protected $acceptanceTester;

    public function __construct(\AcceptanceTester $I)
        $this->acceptanceTester = $I;
    

A pageObject

<?php
// tests/_support/Page/PageA.php
namespace Page;

class PageA

    public static $URL = '/home/pageA';
    public static $title = "page A";

    public static function route($param)
        return static::$URL.$param;
    

    /**
     * @var \AcceptanceTester;
     */
    protected $acceptanceTester;

    public function __construct(\AcceptanceTester $I)
        $this->acceptanceTester = $I;
    

B pageObject

<?php
// tests/_support/Page/PageB.php
namespace Page;

class PageB

    public static $URL = '/home/pageB';
    public static $title = "page B";

    public static function route($param)
        return static::$URL.$param;
    

    /**
     * @var \AcceptanceTester;
     */
    protected $acceptanceTester;

    public function __construct(\AcceptanceTester $I)
        $this->acceptanceTester = $I;
    

然后,我创建了三个 stepObjects ; homeChecker, goToPageA, goToPageB

homeChecker stepObject

<?php
// tests/_support/Step/Acceptance/HomeChecker.php

namespace Step\Acceptance;
use Page\Acceotance\HomePage;

class HomeChecker extends \AcceptanceTester

    /**
     * @Given The home page
     */
    public function main()
    
        $homePage = new PageHome($this);

        $this->amOnPage($homePage::URL);
        $this->checkTitle($homePage);
        $this->checkButtons($homePage);
    

    private function checkTitle($homePage)
        $this->seeInTitle($homePage::$title);
    

    private function checkButtons($homePage)
        $this->see($homePage::$aButton);
        $this->see($homePage::$bButton);
    

PageAChecker stepObject

<?php
// tests/_support/Step/Acceptance/PageAChecker.php

namespace Step\Acceptance;
use Page\PageHome;
use Page\PageA;

class PageAChecker extends \AcceptanceTester

    /**
     * @When I click on the button A
     */
    public function clickButton()
    
        $homePage = new PageHome($this);
        $this->click($homePage::$aButton);
    

    /**
     * @Then l go to the page A
     */
    public function checkTitle()
    
        $aPage = new PageA($this);
        $this->seeInTitle($aPage::$title);
    


PageBChecker stepObject

<?php
// tests/_support/Step/Acceptance/PageBChecker.php

namespace Step\Acceptance;
use Page\PageHome;
use Page\PageB;

class PageBChecker extends \AcceptanceTester

    /**
     * @When I click on the button B
     */
    public function clickButton()
    
        $homePage = new PageHome($this);
        $this->click($homePage::$bButton);
    

    /**
     * @Then l go to the page B
     */
    public function checkTitle()
    
        $bPage = new PageB($this);
        $this->seeInTitle($bPage::$title);
    


现在,我不知道我必须做什么。如果我清空我的 AcceptanceTester 文件并再次运行“./vendor/bin/codecept 运行接受”命令,则测试不完整,并且我在 shell 中收到“未在上下文中找到”警告:

我该怎么办?

更新 我在这里的 codeception GitHub 中创建了一个帖子:

https://github.com/Codeception/Codeception/issues/5157

我描述了一个重现我的问题的最小示例和一个(非常)丑陋的解决方案。我正在寻找一个好方法并理解为什么我描述的不起作用!

【问题讨论】:

尝试将依赖项作为方法参数传递,例如function clickButton($homePage PageHome) $this-&gt;click($homePage::$bButton); . 感谢您的帮助 :) 我像您说的那样进行了更改,但我得到了相同的输出...(“在上下文中找不到 I click on the button B 的步骤定义”) 我相信您在上述函数的 /**/ cmets 中设置的消息正在被您的工具解析。你在那里放了@符号,也许你不应该那样做? 我在创建 *** 帖子时收到了这些消息:/ 帮助我的救星哈哈 这看起来很复杂。 “页面对象”和“步骤对象”本质上不是一回事吗?两者都是您一遍又一遍地做某事的速记符号。将它们结合起来就像进入兔子洞一样。测试应该很容易理解。或者它有什么意义。 【参考方案1】:

我在 shell 中收到“未在上下文中找到”警告

好的,如何将小黄瓜文件的执行与我自己的上下文类(PageObjects、StepObjects、...)中定义的步骤联系起来?我们可以阅读 Codeception 文档中的“BDD > Configuration”章节:

正如我们之前提到的,步骤应该在上下文类中定义。默认情况下,所有步骤都在 Actor 类中定义,例如 AcceptanceTester。但是,您可以包含更多上下文。这可以在全局 codeception.yml 或套件配置文件中配置:

gherkin:
    contexts:
        default:
            - AcceptanceTester
            - AdditionalSteps
            - PageHome
            - HomeChekcer

(...) 这样,PageObjects、Helpers 和 StepObjects 也可以成为上下文。


更好

如果我们继续阅读:

但最好通过标签或角色来包含上下文类。

这意味着,考虑到可扩展性和良好的组织性,您不会希望使用每个页面对象来超载每个测试。因此,您可以按角色、标签或路径分配页面对象(或任何助手类)。请参阅文档的下一段。按照您的示例,并按标签分配:

gherkin:
   contexts:
      default:
         - AcceptanceTester
      tag:
         myTagX:
             - Page\Acceotance\HomePage\HomeChecker
             - Page\PageHome
         anotherTag:
             - Page\Acceotance\another\AnotherChecker
             - Page\PageAnother

...在小黄瓜文件中:

@myTagX
Feature
(...)

【讨论】:

以上是关于Codeception,使用 pageObject 设计模式和 gherkin 编写验收测试的主要内容,如果未能解决你的问题,请参考以下文章

Yii2中如何使用CodeCeption

Yii2中如何使用CodeCeption

如何在 Codeception 中使用 Stub::update?

如何在 Codeception\Util\Stub 中正确使用 atLeastOnce 方法?

使用 Xdebug 调试 Codeception 测试

Codeception:哪些测试组组合是可能的?