ogurets 3.1.5

ogurets #

ogurets is a Gherkin + Cucumber implementation in Dart, focused on making your life writing tests as easy as possible, with the minimum of boilerplate fuss.

It is a fork of the excellent source base of dherkin2 but as that project appears to be dead and we cannot release any further versions, it has been renamed to ogurets (огурец) because it sounds cool and it means cucumber.

For information on Gherkin syntax and Behavior Driven Development (BDD) as a general topic, please see: http://cukes.info/

This is one of a few projects that will be released under this organisational banner - with Flutter and IntelliJ support.

ogurets (like dherkin2) supports standard syntax, scenario outlines with data tables, examples and hooks. It also adds support for (and preference for) Cucumber Expressions.

authors #

This is based on the original work of Dherkin2, along with some modifications made to make it Dart 2 compatible by others.

The current authors are:

an overview of ogurets #

ogurets carries on from dherkin2 in a number of ways:

  • ogurets is based on Dart 2.2+ and is entirely async/await aware. It expects your functions to be async functions.
  • offers support for Cucumber expressions of {string}, {int} and {float} instead of having to write regular expressions.
  • supports an Object (OguretsOpts) that defines what features are run, what steps to use and shared instances
  • supports steps in classes that have positional instances and shared instances passed to them. This allows you to have a global variable that stays for the entire run and ones that get recreated for each session (such as a scenario session). It is a basic form of Dependency Injection.
  • there is an IntelliJ plugin that gives you support for navigation, step creation, run configurations, and displaying the results of test runs.
  • allows overriding of what is actually being run via environment variables (used by the IntelliJ plugin)
  • added support for @Before and @After hooks with optional tags
  • reports allow flexible reporting style. A basic one and an IntelliJ one are included - and you can chain them or have multiple of them as you wish.
  • fields are extracted out of the example rows so you don't have to.

You can still use your existing Dherkin2 style tests and continue by extending with class based tests.

ogurets extensions #

  • ogurets-flutter
  • ogurets IntelliJ plugin

Usage #

Ogurets based Cucumber tests are closely modeled on Java style Step-def classes with annotations. There is @Given, @When, @Then, @And and @But, all of which aid you in writing your standard Cucumber based set of tests.

An example of a Cucumber feature in Ogurets:

Feature: simple addition feature

  Scenario: A simple addition example
    Given I add 4
    And I add 3
    Then the total should be 7

To ensure this test runs, you will need to create a Stepdef class that implements these steps:

import 'package:ogurets/ogurets.dart';

class MyStepdefs {
  @Given(r'I add {int}')
  void iAdd(int toadd) async {
    // Write code here that turns the phrase above into concrete actions

  }

  @Then(r'the total should be {int}')
  void theTotalShouldBe(int total) async {
    // Write code here that turns the phrase above into concrete actions

  }
}

NOTE: if you are using the IDEA plugin, you can just use Alt-Enter and it will create them for you.

To allow us to use some state which exists only for the scenario, lets go and create ourselves a scenario state class.

class ScenarioState {
  int total = 0;
}

And get Ogurets to create it for each scenario run and pass it to us, the whole stepdef becoming:

import 'package:ogurets/ogurets.dart';

import '../lib/scenario_state.dart';

class MyStepdefs {
  final ScenarioState state;

  MyStepdefs(this.state);

  @Given(r'I add {int}')
  void iAdd(int toadd) async {
    state.total += toadd;
  }

  @Then(r'the total should be {int}')
  void theTotalShouldBe(int total) async {
    assert(total == state.total);
  }
}

When run using Ogurets, this gives something like this:

Feature: simple addition feature # test/features/add.feature:1


	Scenario: A simple addition example # test/features/add.feature:3

		I add 4	 # test/features/add.feature:4

		I add 3	 # test/features/add.feature:5

		the total should be 7	 # test/features/add.feature:6

-------------------
Scenarios passed: 1

==================
Features passed: 1

You can also use Scenario Outline style Gherkin tests to achieve the same effect, but in a table.

  Scenario Outline: A simple addition example
    Given I add <amt1>
    And I add <amt2>
    Then the total should be <total>
    Examples:
      | amt1 | amt2 | total |
      | 4    | 3    | 7     |

Run #

ogurets can be executed in a number of ways. In all case, you need to ensure the vm-flag --enable-asserts is passed to dart to ensure your assertions throw exceptions. For example:

dart --enable-asserts test/ogurets_run.dart

If you do this from IDEA, it automatically adds it for you.

ogurets Custom Runner #

You create a new OguretsOpts and give it your features (individual files or recursed folders) and tell it to run. You can tell it to not fail on missing steps, turn debug on, provide instances that will live across all tests.

NOTE: if you are using the Ogurets IntelliJ IDEA plugin, this will be automatically generated for you.

void main(args) async {
  var def = new OguretsOpts()
   ..feature("example/gherkin")
   ..debug()
   ..instance(new SharedInstance())
   ..failOnMissingSteps(false)
   ..tags("~@dataload")
   ..step(Backgrounds)
   ..step(SharedInstanceStepdef)
   ..step(SampleSteps);

  await def.run();
}

Your classes can be constructed so as to take classes that are either defined in the OguretsOpts or they are dynamically constructed at runtime. If they themselves depend on a class it will cycle through creating the entire tree. "Cucumber Expressions" will be turned into regexs as the code is walked through.

e.g.

class Expressions {
     ScenarioSession _session;
     
     Expressions(this._session);
   
     @Given("I have a {string} with {float}")
     void strFloat(String s, num f) {
       _session.sharedStepData[s] = f;
     }

NOTE: You cannot include anything in the constructor that it does not know about.

ogurets hooks #

Hooks work largely like you would expect them to. You can:

  • specify a tag or not. If not, then the before or after will run on every scenario triggered.
  • specify instances to be injected, including the OguretsScenarioSession that holds details about the current scenario.

NOTE: no optional parameters are allowed as there is no "context".

import 'package:ogurets/ogurets.dart';

import 'scenario_session.dart';

class Hooks {
  @Before()
  void beforeEach(ScenarioSession session) {
    session.sharedStepData['before-all'] = 'here';
  }

  @After()
  void afterEach(ScenarioSession session) {
    session.sharedStepData['after-all'] = 'here';
  }

  @Before(tag: 'CukeExpression')
  void beforeExpression(ScenarioSession session) {
    session.sharedStepData['before-expr'] = 'here';
  }

  @After(tag: 'CukeExpression')
  void afterExpression(ScenarioSession session) {
    session.sharedStepData['after-expr'] = 'here';
  }
}

ogurets tags #

Tags also work as per the Cucumber style, where they can be on a feature or on a scenario. If tags are passed they are specifically honoured, if they aren't then all scenarios will be run.

Using the ~@tag syntax prevents the tagged scenario or feature from being run, leaving all others open. Combining ~@tag and @tags leads to non-deterministic behaviour.

@dataload
Feature: load the sample data via the api

  @superuserload
  Scenario Outline: There should be superusers loaded
    Given the system has been initialized
    And I am logged in as the initialized user
    When I register a new user with email "<email>" and groups "<groups>"
    And complete their registration with name "<name>" and password "<password>" and email "<email>"
    Then the user exists and has superuser groups
    And I can login as user "<email>" with password "<password>"
    Examples:
      | name             | email                  | password    | groups    |
      | Капрельянц Ирина | Ирина@mailinator.com   | password123 | superuser |
      | Irina Southwell  | irina@mailinator.com   | password123 | superuser |
      | Richard Vowles   | richard@mailinator.com | password123 | superuser |

Samples: #

  • running with the command line: --tags @dataload would run all features here,
  • running with --tags @superuserload would run the scenario, ignoring the tag on the feature.
  • running with --tags ~@superuserload would run the scenarios in the feature but it would not run the @superuserload tagged feature.
  • running with --tags ~@dataload would ignore the whole feature and it wouldn't be otherwise examined for positive tags.

failure #

Ogurets relies on simple assertions when doing BDD style testing. Dart provides a rich api for doing comparison, so an assertion library like Fest Assert is largely unnecessary.

  @Given("the total should be {int}")
  void totalShouldBe(int amt) async {
    var calcedVal = (_scenarioSession.sharedStepData["add"] as int);
    assert(amt == calcedVal);
  }

The more detailed expect library provide by the Dart Test library is heavily tied to that library and not usable elsewhere.

cucumberd #

Library comes with an executable runner script cucumberd in bin/ directory. Create symbolic link in a directory on your path, like /usr/local/bin:

cd /usr/local/bin
ln -s path/to/ogurets/bin/cucumberd.dart cucumberd
cd

Execute:

cucumberd example/gherkin/test_feature.feature

Note: cucumberd will auto-include all step definitions in steps/ sub-directory. Ability to add steps source locations via command-line arguments is planned.

ogurets style Custom Runner #

Alternatively, you might opt for writing your own script:

   library my_bdd_runner;

   import 'package:ogurets/ogurets.dart';
   import 'my_step_defs.dart'; // import stepdefs, mandatory since no auto-scanning happens

   main(args) {
     run(args);
   }

   // write your StepDefs below

Invoke the runner : $ dart my_bdd_runner.dart my_gherkin.feature

Anatomy of a stepdef #

A stepdef is a top-level function annotated with one of Gherkin keywords. Such a function can take any number of positional parameters, and up to three optional named parameters.

@And("I am a table step \"(\\w+?)\"")
i_am_a_table(arg1, {exampleRow, table, out}) {
   out.writeln("Executing...${exampleRow['column2']}");
}

Table found on the step will be passed in as table. A scenario outline row will be passed in as exampleRow

Output #

Due to asynchronous nature of execution, output of print statements will not appear near the gherkin step that ran them. For that purpose, optional named parameter out will be injected if the stepdef function states that it takes it. Please use the reporters if you wish to override the syntax.

3.1.5 #

  • logic around tags was causing everything to run even if you specified a scenario name
  • too much print logging

3.1.4 #

  • karthi.kk - reported an issue with tags not working as expected. Tags were not triggering properly on feature level and scenario level as per other cucumber variants.
  • support for ~tags so you can turn off specific tags

3.1.3 #

  • karthi.kk - reported issue with existing dherkin2 table parser which only allowed single word entries in table.

3.1.2 #

  • updated documentation
  • updated the IDEA formatter so it outputs examples correctly
  • ensure each example line in a scenario outline has a separate scenario status so it doesn't prevent other examples from running.

3.1.1 #

  • Introducing ogurets for the first time

1.0.1+1 #

  • Introduced this changelog.

1.0.1 #

  • Upgraded all dependencies.
  • Minimal required Dart SDK is now 2.0.0.
  • Removed log4dart because it is not dart2 compatible. Replaced it with the logging lib from pub.dart.

1.0.0 #

  • The fork removed all future-code and replaced it with async/await. The reason for that is that it can properly be used in dart2 tests.

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  ogurets: ^3.1.5

2. Install it

You can install packages from the command line:

with pub:


$ pub get

Alternatively, your editor might support pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:ogurets/ogurets.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
44
Health:
Code health derived from static analysis. [more]
85
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
67
Learn more about scoring.

We analyzed this package on Aug 21, 2019, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.4.0
  • pana: 0.12.19

Platforms

Detected platforms: other

Primary library: package:ogurets/ogurets.dart with components: io, mirrors.

Health suggestions

Fix lib/ogurets.dart. (-2.96 points)

Analysis of lib/ogurets.dart reported 6 hints, including:

line 14 col 1: Prefer using /// for doc comments.

line 62 col 1: Prefer using /// for doc comments.

line 76 col 10: Don't explicitly initialize variables to null.

line 80 col 10: Don't explicitly initialize variables to null.

line 139 col 3: Prefer using /// for doc comments.

Fix lib/src/output/formatter.dart. (-2.96 points)

Analysis of lib/src/output/formatter.dart reported 6 hints, including:

line 5 col 1: Prefer using /// for doc comments.

line 224 col 11: Use isNotEmpty for Iterables and Maps.

line 313 col 17: Don't explicitly initialize variables to null.

line 336 col 9: Use isEmpty instead of length

line 336 col 56: Use contains instead of indexOf

Fix lib/src/model/feature.dart. (-1.49 points)

Analysis of lib/src/model/feature.dart reported 3 hints:

line 14 col 64: Use = to separate a named parameter from its default value.

line 40 col 21: Use isNotEmpty instead of length

line 61 col 3: Prefer using /// for doc comments.

Fix additional 9 files with analysis or formatting issues. (-8.97 points)

Additional issues in the following files:

  • lib/src/model/scenario.dart (3 hints)
  • lib/src/model/step.dart (3 hints)
  • lib/src/model/table.dart (3 hints)
  • lib/ogurets_core.dart (2 hints)
  • lib/src/gherkin_parser.dart (2 hints)
  • lib/src/status/status.dart (2 hints)
  • lib/src/task.dart (2 hints)
  • lib/src/model/scenario_session.dart (1 hint)
  • bin/cucumberd.dart (Run dartfmt to format bin/cucumberd.dart.)

Maintenance suggestions

Maintain an example.

None of the files in the package's example/ directory matches known example patterns.

Common filename patterns include main.dart, example.dart, and ogurets.dart. Packages with multiple examples should provide example/README.md.

For more information see the pub package layout conventions.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.2.0 <3.0.0
ansicolor ^1.0.2 1.0.2
args ^1.5.0 1.5.2
intl ^0.15.8 0.15.8
logging ^0.11.3+2 0.11.3+2
sprintf ^4.0.0 4.0.2
Transitive dependencies
path 1.6.4