You are reading the documentation for gurkencheck 0.0.10. Go to version 0.0.10.

gurkencheck A linter for Gherkin feature files.

Feature file statistics

A suite of feature files grows one scenario at a time, so nobody knows what it adds up to. gurkencheck stats counts it: how many test cases really run, how much of the step vocabulary is shared, and where the same sentence has been written two ways.

Running it#

The linter tells you what is wrong with your feature files. The same files answer a different question — how much is there, and how much of it is the same thing written twice — and stats is the command that asks it. There is nothing to configure.

npx gurkencheck stats features

With no paths given it searches the current directory, the same way the linter does. The word stats has to come first, before any option. Written anywhere else it is read as a path rather than as a command, so gurkencheck --format json stats reports that there is no such file — or quietly lints a directory, if one of that name happens to exist.

The report goes to stdout, so gurkencheck stats > report.txt works. It exits 0 whatever it finds. Statistics describe a suite rather than judge it, and nothing here should ever be the reason a build goes red.

The report#

This is the whole of it for a small shop's worth of feature files. Every list in it is the head of a longer one, cut to the number of entries --top allows and marked with how many were left out; this run asked for three, to keep it short.

9 files, 9 features, 38 test cases

Inventory
  Features              9
  Rules                 2
  Backgrounds           8
  Scenarios            23
  Scenario Outlines     5
  Examples tables       5   15 rows
  Steps               104   as written
  Data tables           0
  Doc strings           0

Scenarios
  Test cases           38   one per Scenario, one per Examples row
  Steps per scenario   min 2   median 3   p90 4   max 4   mean 3.3   Background steps excluded

  Longest
    4  Adding the first item         features/basket.feature:7
    4  Checking out with no address  features/checkout.feature:15
    4  Choosing next day delivery    features/delivery.feature:4
    … and 25 more scenarios

Steps
  Written          104   Background steps included
  Distinct          63   61% of all steps - lower is more reuse
  Written once      43   68% of distinct steps
  Words per step   min 3   median 6   p90 7   max 10   mean 5.7
  Keywords         Given 39 (38%)   When 29 (28%)   Then 36 (35%)   And and But resolved to what they follow

  Most used
    8  i am signed in as ""         features/basket.feature:5
    6  i have 0 items in my basket  features/checkout.feature:6
    4  i check out                  features/checkout.feature:12
    … and 17 more steps

  Written once
    i add "" to my wishlist            features/wishlist.feature:8
    i add <> of "" to my basket        features/basket.feature:26
    i am asked for a delivery address  features/checkout.feature:18
    … and 40 more steps

  Nearly the same (6 groups)
    8  i am signed in as ""  features/basket.feature:5
    1  i'm signed in as ""   features/wishlist.feature:5

    6  i have 0 items in my basket  features/checkout.feature:6
    2  i have 0 item in my basket   features/basket.feature:14

    4  i have a delivery address on file   features/checkout.feature:11
    1  i have no delivery address on file  features/checkout.feature:16
    … and 3 more groups

Tags
  Written              13
  Distinct             10
  Written once          7   a tag written once is often a typo of one written often
  Untagged scenarios    0   0% of scenarios, counting inherited tags

  Most used
    2  @account
    2  @checkout
    2  @slow

  Written once
    @basket, @delivery, @payment
    … and 4 more tags

The sections below walk it in the order it prints, because several of the numbers count something slightly different from what their name suggests.

Test cases, not scenarios#

These files hold 23 Scenarios and 5 Scenario Outlines, and the suite runs 38 tests. An Outline is not one test. It is one per row of its Examples table, and the five tables here carry 15 rows between them. That is the figure that predicts how long a run takes, and it is almost always higher than the one people carry in their heads.

An Outline that has no Examples table yet counts as one, which is how max-scenarios-per-file counts it as well, so the two never disagree about the size of a suite.

Two numbers called steps#

The report prints 104 steps written and a median of 3 steps per scenario, and the two do not reach each other: 104 steps across 28 scenarios would be 3.7 apiece. They count different things. The 104 is every step in the files, Background steps included — 11 of them here — because every one of them still needs a step definition behind it. The steps per scenario leave Backgrounds out, because a Background is written once and read many times, and charging it to every scenario would make a tidy suite look long-winded.

The summary line is four real numbers and an average: the min, median, p90 and max are each a step count that some scenario in your files actually has, and the mean is not. A mean of 3.3 on its own would hide a forty-step scenario; the Longest list underneath names it and says where it is.

What makes two steps the same step#

Distinct is the number worth watching, and what it means depends entirely on when two steps count as one. I have 3 items in my basket and I have 17 items in my basket are one step behind one step definition, and counting them as two would make every measure of reuse meaningless.

So before two steps are compared, the parts a step definition would capture are replaced by a marker. A number becomes 0, a double quoted string becomes "", and a Scenario Outline placeholder becomes <>. Case, repeated spaces, and a full stop or an exclamation mark at the end are ignored. The keyword is left out altogether, so a Given and an And of the same sentence are one step.

Single quotes are left exactly as they are. the user's basket is 'empty' has three of them, and a rule that paired them up would eat half the sentence. Cucumber's own expressions quote with " in any case.

Each kind of argument keeps a marker of its own, so the delivery cost is 0 and the delivery cost is <> stay apart. That understates reuse a little in a suite full of Outlines, and it is the trade worth making: the normalised text is what the report shows you, and a 0 in a step that has no number in it would be a lie about the file. The next section puts the two back together.

A low share of distinct steps means the team shares a vocabulary. A high one means everybody invents their own phrasing, and the step definitions rot.

Steps written two ways#

This is the part of the report you can act on the same afternoon. Each group is one behaviour that is costing you more than one step definition.

  Nearly the same (6 groups)
    8  i am signed in as ""  features/basket.feature:5
    1  i'm signed in as ""   features/wishlist.feature:5

    6  i have 0 items in my basket  features/checkout.feature:6
    2  i have 0 item in my basket   features/basket.feature:14

    4  i have a delivery address on file   features/checkout.feature:11
    1  i have no delivery address on file  features/checkout.feature:16

    2  my basket has 0 item   features/basket.feature:10
    1  my basket has 0 items  features/basket.feature:16

    2  the delivery cost is 0   features/delivery.feature:8
    1  the delivery cost is <>  features/delivery.feature:20

    1  the basket total is 0   features/basket.feature:11
    1  the basket total is <>  features/basket.feature:27

Two steps are grouped when at most three single character edits turn one into the other, and when those edits are no more than about a seventh of the longer of the two. Steps shorter than eight characters are left out, because at that length almost everything looks like everything. Three edits is deliberately tight: further apart than that and a step is a different sentence rather than the same one spelled two ways.

A group may be a chain. Where one step is close to a second and the second is close to a third, all three are reported together even though the first and the third are not close to each other, so a phrasing that drifted over three years arrives as one group rather than two.

Nothing on the command line changes how close two steps have to be. A script can: collectStatistics takes a similarity setting.

Tags nobody agreed on#

Seven of the ten tags here are written exactly once. Some of those are deliberate. The rest are what a tag looks like when it was typed from memory: @wishlst sits in that list a few characters from @wishlist, and a run filtered on either of them quietly misses the other's scenarios.

Untagged scenarios counts the ones carrying no tag of their own, none on their Examples tables, and none inherited from their Feature or their Rule, and so reachable by no tag expression at all. A tag on a Feature covers every scenario inside it, which is why that figure is zero here and often is.

Keeping the report beside a build#

The text report is for reading. The other two formats are for keeping and for showing to somebody else.

npx gurkencheck stats features --format json > stats.json

--format json writes the whole dataset with none of the lists cut short: every distinct step, every scenario, every group, and every file that could not be read. It is indented rather than packed onto one line, so two runs of it can be read side by side and a build can keep a record of what the suite looked like when it went out. --top does not apply to it.

--format md writes the same report as Markdown tables, for pasting into a pull request where the person who has to agree to the work is not going to run the command themselves.

Options#

OptionWhat it does
-f, --formatOutput format: text (the default), json or md. These are the stats command's own formats; the linter's do not apply here.
-i, --ignoreComma separated globs to skip. Replaces .gurkencheckignore rather than adding to it.
-l, --languageThe dialect to read files in when they carry no # language: header. This command does not read .gurkencheckrc, so a language key there does not reach it.
--topHow many entries each list in the text and Markdown reports shows. Ten by default, and it changes nothing under --format json, which never cuts a list short.
-h, --helpShow the options.

Everything the command can refuse is refused before a file is read: a format that does not exist, a --top that is not a whole number, a language code that is not a dialect, an argument that is neither a feature file, a directory nor a glob. Any of those exits 2. A path that is well formed but matches nothing is not one of them — the report says No feature files found. and exits 0 — so a directory renamed out from under a build script goes unnoticed rather than failing it. Once a report has been produced it exits 0, whatever the numbers say.

What is not counted#

A file the parser refuses is listed at the end of the report and counted nowhere in it. Half a broken file is worse than none of it, because every number above it would quietly be wrong. That list is wider than files with a syntax error in them: it also holds the ones breaking a rule Gherkin itself enforces — two Features in one file, a second Background, a tag on a Background — which are the 5 always-on rules.

Nothing on this page has an opinion. There is no threshold to cross, no figure that turns the report red, and no advice about what a good reuse number looks like — a target invented here would be quoted back as a standard. The report says lower is more reuse and stops there. What to do about it is yours, and holding a team to it afterwards is what the rules are for.