GithubHelp home page GithubHelp logo

multi-cuke's Introduction

multi-cuke

multi-cuke is an implementation for parallelized Cucumber-js testing using Node's child_process.spawn API.

It utilizes the Gherkin JS module as a parser to determine the entire set of scenarios that fit the passed arguments and spins up workers to run each- up to the number of available OS processor, or alternatively the passed number of workers (lesser of the two). As a test worker ends, a new worker is instantiated to handle the next scenario on the stack, until empty.


If you need help

Please reach out in our Gitter chat if you have questions, or feel free to open an issue.


Developing with multi-cuke

multi-cuke is written in es6 that is transpiled via Babel. This happens on npm-install, where the compiled code is output to the distribution folder. If making changes, npm run build will re-compile the code. multi-cuke uses travis-ci for linting and unit testing, which performs npm test on all PR's prior to merging with the expectation that they pass.

Using multi-cuke from another Node module

multi-cuke is easily called from within your NodeJS source like any other NPM module:

// Using Babel/es6
import multicuke from 'multi-cuke';

// Using Require 
const multicuke = require('multi-cuke').default;
multicuke();

multi-cuke is Promise-based, and resolves a promise containing the exit code and the outputHandler when all have finished running. The outputHandler is returned as the summary data of the overall test suite run can be used in conjuction with other runs, if you would like to amass data across different test suites (See below for options and command-line args). Running from the command line will output the summary data then auto-exit with the returned exit code, while calling multi-cuke from a node module returns the promise that resolves to an exit code to be handled at your discretion. The promise is not rejected due to test scenario failures, but is rejected on errors in test execution to differentiate and provide clarity.

multi-cuke takes an options object as a parameter, but when none is passed will default to standard options, which are:

{
  'paths': ['features'],
  'tags': [],
  'requires': [],
  'cucumberPath': require.resolve("cucumber"),
  'workers': require('os').cpus().length,
  'workerEnvVars': {},
  'logDir': '.tmp-logs',
  'silentSummary': false,
  'verbose': false,
  'inlineStream': false,
  'failFast': false,
  'devMode': false,
  'strict': false
}

The options object passed is extended with default values via lodash's _.default() utility, so passing all options are not required, and passing simply

{ paths: ['otherFeatureDir'] }

or

{
  tags: ['@Smoke'],
  workers: 4
}

is as valid as passing all options.

paths expects an array of paths which is will use as the source of your tests.

tags expects an array of cucumber-js tags to run a subset of all features found.

requires follows the --requires tag as defined by cucumber-js, and specifies external sources for step definitions, support files.

cucumberPath is the path to your cucumber-js module, if you need to use a specific version, otherwise will default to what is included in multi-cuke.

workers specifies the max number of parallel workers to be running at once. If no number passed, defaults to the number of cpu's available.

workerEnvVars is an object that will be passed as environment variables to the cucumber-js worker process, and it's properties will be available to access in the spawned process via process.env. The values for each key must be a string.

logDir specifies the dir multi-cuke writes test log files to.

The silentSummary option silences the default output of summary data when all tests cases resolve. This is an optional flag so that output summary can be handled or transformed at a later time via the returned object in the promise. An example:

const multicuke = require('multi-cuke');
multicuke().then((results) => {
  // results.outputHandler is the parser defined for this test
  console.log(results.outputHandler.getSummaryOutput());
});

Or to combine data of multiple test suites (via tags):

const multicuke = require('multi-cuke');

Promise.all([
  multicuke({ tags: ['@Blocker'] }),
  multicuke({ tags: ['@LowUserPath'] })
]).then((results) => {
  // Deal with results ...
});

verbose will log extra data around which workers are queued and still running.

inlineStream will remove the silence on the worker process, so output occur in real-time in addition to the multi-cuke output. Generally, this makes output hard to decipher with workers running asynchronously, but the option is included for aid in debugging if deemed necessary or helpful.

devMode will run tests serially by calling the Cucumber-js cli directly. This option can be used during test development so that you can see the output update with each step run (avoiding the double printing that inlineStream shows).

Using multi-cuke from command line

multi-cuke comes ready to use from command line. It supports arguments of both feature paths and directory paths that contain features (including multiple paths), as well as the following tags:


  -t, --tag               Scenario tag, as defined by cucumber-js
  -r, --require           Require flag, to specify non-default files to require, as defined by cucumber-js
  -c, --cucumber          Specify using a specific cucumber installation
  -w, --workers           Number of workers in parallel at a given time (defaults to the number of processors if none passed).
  -l, --logdir            Output dir for test logs
  -s, --silent-summary    Silences summary output so it can be handled via the returned promise
  -v, --verbose           Adds verbose output to console
  -i, --inlinestream      Inlines stream in real time in addition to multi-cuke output. *Note* This adds complexity to the logs that are hard to decipher, but included if needed for debugging
  -d, --devMode           Shortcut to running cucumber-js directly
  --fail-fast             Abort the run on first failure
  --strict                Fail if a step definition is not defined

All of the above options can also be found by using the --help flag on the command line.

Examples valid command from the command line (assuming multi-cuke is globally installed with npm install -g multi-cuke):

With default options, being run inside a directory with a features directory containing feature files.

multi-cuke

Specifying a specific path to feature files, and using only the @Smoke tag

multi-cuke path/to/features -t @Smoke

(Multiple) specific individual feature files

multi-cuke some-features/test1.feature other-feature/test2.feature

It does not support the formatter flag currently available in cucumber-js' CLI, as parsing of the output of multiple concurrent jobs acts differently than a single thread. See below for more information.

It is important to note that multi-cuke defers to the installed version of cucumber-js unless otherwise passed a path to another cucumber install. To use a specific/pinned version of cucumber in your project, simply pass it on the command line or include it in the options object, and that will be used in place of the local dependency installed with multi-cuke.

Using console from within multi-cuke

With the output channels of the child process specifically silenced in order to keep scenario logs in tact, console.log, error, etc. will not display during a test's run. To address this, there is an included utility utils/worker-log-handler.js. The handler is provided so that you can declare it in your world.js file, and then be able to access it from your step definitions with a simple replace:

// world.js
this.log = require('multi-cuke/distribution/utils/worker-log-handler');


// steps.js
this.log('Log as usual, %s', 'nice');

This reason for this handler is to prevent having to rewrite logic in multiple projects. With this handler, if you are not within a child process (and therefore not parallelized), it will simply console.log as expected. If it is within a child process it will send a message to the test handler, which will push the message to the worker's defined log array, which will be batch output in order once the test finishes.

Differences from standard Cucumber-js

To best consolidate the data of all scenario runs into meaningful test results, multi-cuke runs Cucumber with the json formatter, and the results parsed back to pretty formatting for readability by lib/parsers/pretty. The standard cucumber-js formatters would have formatting and/or redundancy issues, so are not supported (at this time) and ignored here. Additional parsers can be added using the same API as defined in lib/parsers/pretty.

multi-cuke also explicitly silences the stdio channels of the workers. Errors are still caught and surfaced in the worker's execution, but this prevents the test/step definitions from throwing errors to stdout in real-time as other tests run in parallel (and potentially creating very unclear logs). Additional/debug logging past error handling can alternatively be handled by having a debug log piped to file from step definitions, which serves a better purpose than writing to stdout and would not be affected by multi-cuke.

Additionally, since errors are caught and handled per-scenario basis, exceptions that would otherwise exit the process of Cucumber-js when found in a given scenario will be contained to that scenario's process, output, and logged- but will not stop the execution of other tests unrelated to that issue.

Contributions welcome.

multi-cuke's People

Contributors

midniteio avatar vsiakka avatar guykisel avatar efokschaner avatar dristic avatar zrnorth avatar

Stargazers

Leonardo Dimarchi avatar Sissel avatar Karl Schmidt avatar Derek Sudduth avatar Alessio Coltellacci avatar Simon Tegg avatar John Hill avatar  avatar J. Elliot Taylor avatar Aaron Evans avatar  avatar  avatar Johan Ochoa avatar

Watchers

 avatar Johan Ochoa avatar  avatar  avatar  avatar

multi-cuke's Issues

Multicuke returns 0 for invalid feature files, but cucumber-js returns 1

If the feature files we try to run are all invalid due to bad syntax, multi-cuke returns a null exit code, but for the same files, cucumber-js returns 1. It might be better if multi-cuke was consistent with cucumber-js.

(also I realize this is intentional behavior, but I still wonder if it might be better to be consistent with the tool we're wrapping - example use case is that someone updates all of the feature files but accidentally leaves in merge conflict garbage, rendering all feature files invalid - the tests would all just pass and you might never notice unless you glanced at console logs)

Examples tags on Scenarios Outline are not supported

This feature is supported by Cucumber :

@06_simulation_check_outline
Scenario Outline: (OUTLINE_) BAT 06 - Simulation Check (

, ,
Given I am on CPA Login page
And I login with "RANDOM ACCOUNT"

@06_simulation_check_outline_1
Examples:
  | SECTION      | UNIT   |  SIMULATION |
  | FINANCIAL    | F1     |  1          |
@06_simulation_check_outline_2
Examples:
  | SECTION      | UNIT   |  SIMULATION |
  | REGULATION   | R1     |  1          |

If you run multi-cuke by one of the example tags (@06_simulation_check_outline_1), a message says that "There are no scenarios found that match the options passed.".

Also, if you run the tests using the scenario tag @06_simulation_check_outline, this error is shown:

Unhandled rejection TypeError: Cannot read property 'cells' of undefined
at new Worker (/Users/beckerqa/Documents/CPA2017/node_modules/multi-cuke/distribution/lib/worker.js:74:10)
at TestHandler.createWorker (/Users/beckerqa/Documents/CPA2017/node_modules/multi-cuke/distribution/lib/test-handler.js:169:20)
at /Users/beckerqa/Documents/CPA2017/node_modules/multi-cuke/distribution/lib/test-handler.js:101:20
at tryCatcher (/Users/beckerqa/Documents/CPA2017/node_modules/bluebird/js/release/util.js:16:23)
at Promise._settlePromiseFromHandler (/Users/beckerqa/Documents/CPA2017/node_modules/bluebird/js/release/promise.js:510:31)
at Promise._settlePromise (/Users/beckerqa/Documents/CPA2017/node_modules/bluebird/js/release/promise.js:567:18)
at Promise._settlePromise0 (/Users/beckerqa/Documents/CPA2017/node_modules/bluebird/js/release/promise.js:612:10)
at Promise._settlePromises (/Users/beckerqa/Documents/CPA2017/node_modules/bluebird/js/release/promise.js:691:18)
at Promise._fulfill (/Users/beckerqa/Documents/CPA2017/node_modules/bluebird/js/release/promise.js:636:18)
at Promise._resolveCallback (/Users/beckerqa/Documents/CPA2017/node_modules/bluebird/js/release/promise.js:431:57)
at Promise._settlePromiseFromHandler (/Users/beckerqa/Documents/CPA2017/node_modules/bluebird/js/release/promise.js:522:17)
at Promise._settlePromise (/Users/beckerqa/Documents/CPA2017/node_modules/bluebird/js/release/promise.js:567:18)
at Promise._settlePromise0 (/Users/beckerqa/Documents/CPA2017/node_modules/bluebird/js/release/promise.js:612:10)
at Promise._settlePromises (/Users/beckerqa/Documents/CPA2017/node_modules/bluebird/js/release/promise.js:691:18)
at Promise._fulfill (/Users/beckerqa/Documents/CPA2017/node_modules/bluebird/js/release/promise.js:636:18)
at Promise._resolveCallback (/Users/beckerqa/Documents/CPA2017/node_modules/bluebird/js/release/promise.js:431:57)

workers cannot exceed number of cores

Passing the argument to the cli of -w 8 does not run 8 tests in parallel, I even tried editing the .js files for main and cli and hard coded the worker defaults to 8 but still only 4 are run.

Should setting the -w argument allow more than 4 to be run?

Thanks

Add verbose mode

With debug output of what is scheduled when, and what is currently running.

Support scenario outlines

multi-cuke doesn't seem to correctly support outlines. If I have this scenario:

Feature: LKG
  @LKG
  Scenario Outline: No-op test
    Given I do nothing

    Examples:
      | iteration |
      |     1     |
      |     2     |
      |     3     |

When I run it in regular cucumber I get:

Feature: LKG

  @LKG
  Scenario: No-op test
    Given I do nothing

  @LKG
  Scenario: No-op test
    Given I do nothing

  @LKG
  Scenario: No-op test
    Given I do nothing

3 scenarios (3 passed)
3 steps (3 passed)

But when I run it in multi-cuke I get:

Feature: LKG
  @LKG
  Scenario: No-op test
    Given I do nothing  # lkg.feature:4

1 scenario (1 passed)
5 steps (5 passed)

Can't identify feature tags

When having a feature:

@BAT_file
Feature: CPA BAT

@01_web_app_build_number
Scenario: BAT 01 - Web App Build Number
Given I am on CPA Login page
And I should be at "LOGIN PAGE"
And I login with "BAT ACCOUNT"

and having this configuration:

const multicuke = require('multi-cuke').default;
var options = {
paths: ['./features'],
tags: ['@BAT_file'],
requires: [],
cucumberPath: "node_modules/cucumber/bin/cucumber.js",
workers: 4,
workerEnvVars: {},
logDir: '.multiCuke_logs',
silentSummary: false,
verbose: false,
inlineStream: false,
failFast: false,
devMode: false,
strict: false
};
multicuke(options);

multi-cuke throws this message: There are no scenarios found that match the options passed.

So, I assume that feature tags are being ignored

parseException fails if exception is undefined

TypeError: Cannot read property 'stack' of undefined
    at PrettyParser.parseException (C:\jenkins\workspace\***\node_modules\multi-cuke\distribution\lib\parsers\pretty.js:125:35)
    at PrettyParser.handleResult (C:\jenkins\workspace\***\node_modules\multi-cuke\distribution\lib\parsers\pretty.js:60:21)
    at done (C:\jenkins\workspace\***\node_modules\multi-cuke\distribution\lib\test-handler.js:151:43)
    at C:\jenkins\workspace\***\node_modules\multi-cuke\distribution\lib\test-handler.js:182:16
    at bound (domain.js:280:14)
    at runBound (domain.js:293:12)
    at tryCatcher (C:\jenkins\workspace\***\node_modules\bluebird\js\release\util.js:16:23)
    at Promise._settlePromiseFromHandler (C:\jenkins\workspace\***\node_modules\bluebird\js\release\promise.js:504:31)
    at Promise._settlePromise (C:\jenkins\workspace\***\node_modules\bluebird\js\release\promise.js:561:18)
    at Promise._settlePromise0 (C:\jenkins\workspace\***\node_modules\bluebird\js\release\promise.js:606:10)
    at Promise._settlePromises (C:\jenkins\workspace\***\node_modules\bluebird\js\release\promise.js:685:18)
    at Async._drainQueue (C:\jenkins\workspace\***\node_modules\bluebird\js\release\async.js:138:16)
    at Async._drainQueues (C:\jenkins\workspace\***\node_modules\bluebird\js\release\async.js:148:10)
    at Immediate.Async.drainQueues [as _onImmediate] (C:\jenkins\workspace\***\node_modules\bluebird\js\release\async.js:17:14)
    at tryOnImmediate (timers.js:543:15)
    at processImmediate [as _immediateCallback] (timers.js:523:5)

JSON output file

Is there any way to generate a json output format for the test execution ?

TypeError: multicuke(options) is not a function

I have runMultiFeature.js having following code :

const multicuke = require('multi-cuke');
var options = {
    'paths': ['./login.feature'],
    'tags': [],
    'requires': ['./ui'],
    'cucumberPath': "node_modules/cucumber/bin/cucumber.js",
    'workers': 4,
    'workerEnvVars': {},
    'logDir': '.multiCuke_logs',
    'silentSummary': false,
    'verbose': false,
    'inlineStream': false,
    'failFast': false,
    'devMode': false,
    'strict': false
};
multicuke(options);

and executing the .js file using "node runMultiFeature.js"

getting following error:
multicuke();
^

TypeError: multicuke is not a function
at Object. (C:\UI_automation_201016\runMultiFeature.js:17:1)
at Module._compile (module.js:413:34)
at Object.Module._extensions..js (module.js:422:10)
at Module.load (module.js:357:32)
at Function.Module._load (module.js:314:12)
at Function.Module.runMain (module.js:447:10)
at startup (node.js:141:18)
at node.js:933:3

Verbose flag should support file output

the --verbose flag on multi-cuke should support no options (e.g: -v --verbose) to print to console, or a filepath to log to file (e.g: -v file://path --verbose file://path)

Support for external parsers

Pretty parser is defined as a class that exposes an API to handle calls around test results and results output. Other parsers would be able to be used as a drop in replacement to handle results however needed, but still would require adding a flag/option to multi-cuke and defining a default parser if none passed.

Timeout based on stdout to prevent hangs

Should support a timeout if the process has received no stdout in a given amount of time. This is to prevent the scenario where cucumber itself hangs or crashes and we stop receiving data back from the child.

Abstract resource/cost system

Allow for a way to pass a function as an abstracted way to determine cost of a scenario, to allow for a scheduling algorithm

Support debug message propagation

With output channels silenced, there is no built in to log to console, as console.log() will not output from step definitions. Should create a handler for messages from child process to allow for easier debugging.

Unit tests fail in windows

  8) Feature Finder should support multiple paths:

      AssertionError: expected [ Array(4) ] to deeply equal [ Array(4) ]
      + expected - actual

       [
         {
      -    "featureFile": "test\\features\\sample.feature"
      +    "featureFile": "test/features/sample.feature"
           "scenarioLine": 7
         }
         {
      -    "featureFile": "test\\features\\sample.feature"
      +    "featureFile": "test/features/sample.feature"
           "scenarioLine": 11
         }
         {
      -    "featureFile": "test\\features\\sample.feature"
      +    "featureFile": "test/features/sample.feature"
           "scenarioLine": 14
         }
         {
      -    "featureFile": "test\\features\\sample2.feature"
      +    "featureFile": "test/features/sample2.feature"
           "scenarioLine": 4
         }
       ]

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.