美文网首页
CucumberJS

CucumberJS

作者: xiaojuanxu | 来源:发表于2018-11-29 17:30 被阅读0次

    Cucumber.js是用JavaScript实现的Cucumber,可以运行在Node.js(4.0及以上版本)和现代web浏览器上。

    Install

    npm install cucumber
    

    Notes: Cucumber不能全局安装,因为使用时需要在support files中引用cucumber,而全局安装的模块是无法被引用的。

    CLI

    Running specific features

    • 运行全部的feature
      • $ cucumber-js features/**/*.feature
    • 运行特定文件夹下的feature
      • $ cucumber-js features/dir
    • 运行特定的feature
      • $ cucumber-js features/my_feature.feature
    • 运行一个特定的Scenario根据它所在的行数
      • $ cucumber-js features/my_feature.feature:3
    • 运行一个特定的Scenario根据它的名字
      • $ cucumber-js --name "topic 1"
    • Tags

    Requiring support files

    在运行features前,用--require <GLOB|DIR|FILE>来引用support file。

    • node-glob
    • 如果不使用GLOB模式,则需要自定义support文件,见下文

    Formats

    --format <TYPE[:PATH]>可以指定输出的格式。如果PATH没有指定,则输出到默认文件中。否则输出到PATH指定的文件。format可以同时指定多个,输出不同格式的结果到不同的文件中。

    • Q1: 不指定PATH的时候,stdout指向哪里?
    • Q2: 如何同时指定多个format到多个file?

    内置的几种格式:event-protocol, json, progress, progress-bar, rerun, snippets, summary, usage, usage-json

    rerun -- prints the paths of any non passing scenarios(example)

    Format Options

    可以使用 --format-options <JSON> 指定format 参数.

    建议的用法: 创建profile文件(见后续),在其中定义一些json对象,用 JSON.stringify调用,避免直接写JSON.

    For example:

    //cucumber.js
    const formatOpt = { "colorEnabled": true };
    
    exports = module.exports = {
        default: `--compiler es6:babel-core/register --format-options '${JSON.stringify(formatOpt)}'`
    }
    

    Colors

    --format-options '{"colorsEnabled": false}'可以使colors失效。

    Exiting

    使用--exit可以在测试运行结束时强制退出,但是不建议这么使用。

    Undefined Step Snippets

    未定义的step片段会默认使用回到接口打印到javascript中。
    重写代码片段可以使用--format-options '{"snippetInterface": "<interface>"}'。有效的interface有async-await, callback, generator, promise 或者 synchronous。

    Rerun separator

    rerun格式的分隔符可以重新指定--format-options '{"rerun": {"separator": "<separator>"}}'

    默认的分隔符是一个newline charactor,当我们需要在本地从CLI日志中复制一行重新运行那些失败的测试而分隔符是一个space charactor时,这个属性是有用的。

    需要注意的是,rerun file的分隔符只能是默认的分隔符。

    Parallel

    使用--parallel <NUMBER_OF_SLAVES>我们可以同时并行多个scenarios。每一个slave都是一个独立的node进程并接受下列变量:

    • CUCUMBER_PARALLEL - set to 'true'
    • CUCUMBER_TOTAL_SLAVES - set to the number of slaves
    • CUCUMBER_SLAVE_ID - id for slave

    **Note: **在使用parallel的时候,打印stdout(比如 console.log)会报错,因为每个从进程会通过stdout与主进程通信。

    Profile

    为了存放和重用那些常用的CLI options,我们可以在根目录下添加一个cucumber.js的文件,把options写在其中。Profile可以通过-p <NAME>--profile <NAME>来调用。

    Tags

    cucumber tag expression

    Transpilers

    Step definitions 和 support file可以用其它语言编写,然后转译成javascript。为了达到这个目的,可以使用--require-module <module_name>

    World Parameters

    我们可以把一些parameter传递到 world constructor中,使用--world-parameter <JSON>. The JSON string必须定义一个对象。

    Support Files

    World

    对每个scenario,World都是一个独立的上下文,暴露给hooks和steps就是this。默认的world构造函数:

    function World({attach, parameter}) {
        this.attach = attach
        this.parameters = parameters
    }
    
    • attach: 用于向hooks/steps添加attachments
    • parameters: 就是通过CLI传递的参数对象

    默认的World可以被覆盖通过setWorldConstructor:

    var {setWorldConstructor} = require('cucumber');
    var seleniumWebdriver = require('selenium-webdriver');
    
    function CustomWorld() {
        this.driver = new seleniumWebDriver.Builder()
        .forBrowser('firefox')
        .build();
    }
    
    this.waitForElement = function(locator) {
        var condition = seleniumWebdriver.until.elementLocated(locator);
        return this.driver.wait(condition);
    }
    
    setWorldConstructor(CustomWorld);
    

    Hooks

    Hooks用于运行每一个scenario前后setup和teardown环境。Multiple Before hooks可以按定义顺序依次执行,而 Multiple After hooks则按照他们定义的顺序反向执行。

    var {After, Before} = require('cucumber');
    
    // Synchronous
    Before(function () {
      this.count = 0;
    });
    
    // Asynchronous Callback
    Before(function (testCase, callback) {
      var world = this;
      tmp.dir({unsafeCleanup: true}, function(error, dir) {
        if (error) {
          callback(error);
        } else {
          world.tmpDir = dir;
          callback();
        }
      });
    });
    
    // Asynchronous Promise
    After(function () {
      // Assuming this.driver is a selenium webdriver
      return this.driver.quit();
    });
    

    Tagged hooks

    var {After, Before} = require('cucumber');
    
    Before(function () {
      // This hook will be executed before all scenarios
    });
    
    Before({tags: "@foo"}, function () {
      // This hook will be executed before scenarios tagged with @foo
    });
    
    Before({tags: "@foo and @bar"}, function () {
      // This hook will be executed before scenarios tagged with @foo and @bar
    });
    
    Before({tags: "@foo or @bar"}, function () {
      // This hook will be executed before scenarios tagged with @foo or @bar
    });
    
    // You can use the following shorthand when only specifying tags
    Before("@foo", function () {
      // This hook will be executed before scenarios tagged with @foo
    });
    

    BeforeAll/AfterAll

    如果需要在所有的scenario前后setup/teardown环境,可以使用BeforeAll/AfterAll。像hooks和steps一样,她们可以是异步的?同步的?接收callback,返回一个promise

    Notes 不同于Before/After,这些方法不能接收world 实例作为this,这是因为每一个scenario都会拥有一个属于自己的world实例。

    var {AfterAll, BeforeAll} = require('cucumber');
    
    // Synchronous
    BeforeAll(function () {
      // perform some shared setup
    });
    
    // Asynchronous Callback
    BeforeAll(function (callback) {
      // perform some shared setup
    
      // execute the callback (optionally passing an error when done)
    });
    
    // Asynchronous Promise
    AfterAll(function () {
      // perform some shared teardown
      return Promise.resolve()
    });
    

    Timeout

    默认情况下,异步的hooks和steps会在5000ms后超时,这个默认值也可以全局修改:

    const setDefaultTimeout = require('cucumber');
    setDefaultTimeout(60*1000);
    

    也可以为一个特定的hook或者step设定timeout:

    var {Before, Given} = require('cucumber');
    
    Before({timeout: 60 * 1000}, function() {
      // Does some slow browser/filesystem/network actions
    });
    
    Given(/^a slow step$/, {timeout: 60 * 1000}, function() {
      // Does some slow browser/filesystem/network actions
    });
    
    

    也可以选择disable掉timeout设置通过设置timeout的值为-1,但相应的要定义自己的超时保护机制,否则测试会意外退出或永久挂起:

    var {Before, Given} = require('cucumber');
    var Promise = require('bluebird');
    
    Given('the operation completes within {n} minutes', {timeout: -1}, function(minutes) {
      const milliseconds = (minutes + 1) * 60 * 1000
      const message = `operation did not complete within ${minutes} minutes`
      return Promise(this.verifyOperationComplete()).timeout(milliseconds, message);
    });
    

    Data tables

    Data tables分为有header和无header两种,具体使用可以参照example

    • with column header
    Scenario: rows
        Given a file named "features/passing_steps.feature" with:
          """
          Feature: a feature
            Scenario: a scenario
              Given a table step
                | Vegetable | Rating |
                | Apricot   | 5      |
                | Brocolli  | 2      |
                | Cucumber  | 10     |
          """
        Given a file named "features/step_definitions/passing_steps.js" with:
          """
          import {Given} from 'cucumber'
          import assert from 'assert'
          Given(/^a table step$/, function(table) {
            const expected = [
              ['Apricot', '5'],
              ['Brocolli', '2'],
              ['Cucumber', '10']
            ]
            assert.deepEqual(table.rows(), expected)
          })
          """
        When I run cucumber-js
        Then it passes
    
    * hashes: returns an array of objects where each row is converted to an object (column header is the key)
    * rows: returns the table as a 2-D array, without the first row
    
    • without column header
     Scenario: raw
        Given a file named "features/passing_steps.feature" with:
          """
          Feature: a feature
            Scenario: a scenario
              Given a table step
                | Cucumber     | Cucumis sativus |
                | Burr Gherkin | Cucumis anguria |
          """
        Given a file named "features/step_definitions/passing_steps.js" with:
          """
          import {Given} from 'cucumber'
          import assert from 'assert'
          Given(/^a table step$/, function(table) {
            const expected = [
              ['Cucumber', 'Cucumis sativus'],
              ['Burr Gherkin', 'Cucumis anguria']
            ]
            assert.deepEqual(table.raw(), expected)
          })
          """
        When I run cucumber-js
        Then it passes
    
    * raw: returns the table as a 2-D array
    * rowsHash: returns an object where each row corresponds to an entry (first column is the key, second column is the value)
    

    Attachments

    文字、图片和其它类型的数据都可以通过attachments添加到时间协议和json格式的输出结果中。具体使用可以参照Attachments

    API Reference

    查询CucumberJS的API通过这个链接API Reference

    相关文章

      网友评论

          本文标题:CucumberJS

          本文链接:https://www.haomeiwen.com/subject/kvvybftx.html