使用 ZombieJS 的 Cucumber.js 场景中的回调问题

Posted

技术标签:

【中文标题】使用 ZombieJS 的 Cucumber.js 场景中的回调问题【英文标题】:Issue with callbacks in Cucumber.js scenario with ZombieJS 【发布时间】:2014-09-03 16:12:30 【问题描述】:

我对 Cucumber 和 Lettuce 等 BDD 工具有一定的经验。我目前正在构建一个 Phonegap 应用程序,我想开始使用 Cucumber.js 为其创建验收测试。不幸的是,我遇到了一些问题。

这是我整理的基本功能文件:

Feature: Authentication

    As a user
    I want to be able to log in and out

    Scenario: Logging in
        Given I am not logged in
        And I am on the page "login"
        When I fill in the "username" field with "student"
        And I fill in the "password" field with "password"
        And I click the "LOG IN" button
        Then I should see the text "STUDENT"

这是我的world.js

var zombie = require('zombie');
var World = function World(callback) 
    "use strict";

    this.browser = new zombie(); // this.browser will be available in step definitions

    this.visit = function (url, callback) 
        this.browser.visit(url, callback);
    ;

    callback(); // tell Cucumber we're finished and to use 'this' as the world instance
;
exports.World = World;

这是我的步骤定义:

var wrapper = function () 
    "use strict";

    this.World = require("../support/world.js").World; // overwrite default World constructor

    this.Given(/^I am not logged in$/, function (callback) 
        // Clear local storage
        this.browser.localStorage("localhost:9001").clear();
        callback();
    );

    this.Given(/^I am on the page "([^"]*)"$/, function (page, callback) 
        // Visit page
        this.browser.visit('http://localhost:9001/app/index.html#' + page, callback);
    );
;

module.exports = wrapper;

我设置了一个 Grunt 任务,它首先在端口 9001 上运行连接服务器,然后运行 ​​Cucumber 场景。 documentation for Cucumber.js implies this should work,但是第二步失败了。

这是我收到的错误消息:

Running "connect:cucumber" (connect) task
Started connect web server on http://localhost:9001

Running "cucumberjs:src" (cucumberjs) task
.Cannot call method 'add' of undefined TypeError: Cannot call method 'add' of undefined
    at <anonymous>:10:711
    at <anonymous>:10:874
    at <anonymous>:10:1224
    at Contextify.sandbox.run (/Users/matthewdaly/Projects/myapp/node_modules/zombie/node_modules/jsdom/node_modules/contextify/lib/contextify.js:12:24)
    at DOMWindow.window._evaluate (/Users/matthewdaly/Projects/myapp/node_modules/zombie/lib/zombie/window.js:188:25)
    at Object.HTML.languageProcessors.javascript (/Users/matthewdaly/Projects/myapp/node_modules/zombie/lib/zombie/scripts.js:23:21)
    at define.proto._eval (/Users/matthewdaly/Projects/myapp/node_modules/zombie/node_modules/jsdom/lib/jsdom/level2/html.js:1480:47)
    at loaded (/Users/matthewdaly/Projects/myapp/node_modules/zombie/lib/zombie/scripts.js:74:23)
    at /Users/matthewdaly/Projects/myapp/node_modules/zombie/node_modules/jsdom/lib/jsdom/level2/html.js:76:20
    at Object.item.check (/Users/matthewdaly/Projects/myapp/node_modules/zombie/node_modules/jsdom/lib/jsdom/level2/html.js:345:11)
FUUUU

(::) failed steps (::)

TypeError: Cannot call method 'add' of undefined
    at <anonymous>:10:711
    at <anonymous>:10:874
    at <anonymous>:10:1224
    at Contextify.sandbox.run (/Users/matthewdaly/Projects/myapp/node_modules/zombie/node_modules/jsdom/node_modules/contextify/lib/contextify.js:12:24)
    at DOMWindow.window._evaluate (/Users/matthewdaly/Projects/myapp/node_modules/zombie/lib/zombie/window.js:188:25)
    at Object.HTML.languageProcessors.javascript (/Users/matthewdaly/Projects/myapp/node_modules/zombie/lib/zombie/scripts.js:23:21)
    at define.proto._eval (/Users/matthewdaly/Projects/myapp/node_modules/zombie/node_modules/jsdom/lib/jsdom/level2/html.js:1480:47)
    at loaded (/Users/matthewdaly/Projects/myapp/node_modules/zombie/lib/zombie/scripts.js:74:23)
    at /Users/matthewdaly/Projects/myapp/node_modules/zombie/node_modules/jsdom/lib/jsdom/level2/html.js:76:20
    at Object.item.check (/Users/matthewdaly/Projects/myapp/node_modules/zombie/node_modules/jsdom/lib/jsdom/level2/html.js:345:11)

如果我在第二步的正文之后插入callback();,它就会通过。我不确定发生了什么事。为什么这个场景会失败?该应用程序本身按预期工作。似乎第二步的回调永远不会触发。

【问题讨论】:

【参考方案1】:

如果您将回调添加到第二步,则测试通过,因为只是跳过了 visitPage。

我的访问函数是这样的:

 this.visit = function(url, callback) 
   that.browser.visit(url, function(error) 
     if (error) 
       callback.fail(error);
      else 
       callback.call(that, that.browser);
     
   );
 );

但我认为真正的问题出在您的页面上,因为 sandbox.run 是僵尸开始从页面执行自定义 (js) 代码的地方。所以这是您(缩小)脚本中第 1224 列中的匿名回调? 也许你必须用 console.log 来追踪它...(localStorage 的东西?虽然僵尸支持它),在你的自定义代码中 grep 为“添加”

【讨论】:

【参考方案2】:

为什么要使用回调?他们混淆了你的代码。然而,等效的是使用 async/await 对,可以说,这将模仿 java 编码和正确的指令开始和结束:

var R = await visit () ;
await do_this_when_visit_is_done () ;
await do_that_when_do_this_is_done() ;

in cucumber :
this.Given(/^I am on the page "(.*)"$/, async function (page) 

  await this.page_is_loaded() ;

【讨论】:

谢谢。我的回答所响应的范围并不比我们在这里看到的问题稍微大一点,即回调可能难以调试或读取,所以我想提到这个解决方案作为一个更好的解决方案,恕我直言,异步编码的实现,反过来可能有助于更轻松地调试当前问题。

以上是关于使用 ZombieJS 的 Cucumber.js 场景中的回调问题的主要内容,如果未能解决你的问题,请参考以下文章

Node.js BDD。黄瓜,小黄瓜还是黄瓜? [关闭]

如何在cucumber.js的AfterStep钩子中获得步骤结果

javascript cucumber.js的步骤实现

无法在 Windows 7 64 位上安装 WebdriverIO Cucumber JS 框架

CucumberJS 资源

带有量角器的黄瓜 HTML 报告