Skip to content

Predefine steps

Since 4.6.0 vitest-cucumber allowes to define step globally, useful for scenario / background with same steps.

Gherkin examples :

Feature: use predefined steps in unit tests
    Background:
        Given I've scenarii with same steps
        And   I use Rule and Feature
    Scenario: scenario with docStrings
        Given I use docStrings
        """
        I love DocStrings
        """
        When  I run unit tests
        Then  I can use it
    Rule: predefined steps in rule
        Scenario: scenario without docStrings
            Given I don't use docStrings
            When  I run unit tests
            Then  I use scenario context

In all examples with defineSteps, you can define all step types: Given, When, Then, And and But.

Currently you can define a step never used in scenario, vitest-cucumber doesn’t throw an error.

vitest-cucumber still throw an error if steps are missing in scenario and background.

You can define globally, by example in vitest.setup.ts:

import { defineSteps } from "@amiceli/vitest-cucumber"

defineSteps(({ When }) => {
    When('I run unit tests', () => {})
})

You also can define steps only for a feature :

import { defineSteps, loadFeature, describeFeature } from "@amiceli/vitest-cucumber"

const feature = await loadFeature('feature/example.feature')

describeFeature(feature, (f) => {
    f.defineSteps(({ And }) => {
        And('I use Rule and Feature', () => {
            expect(f.context.init).toBe(true)
        })
        Given('I use docStrings', (ctx, docStrings: string) => {
            // you can access on docStrings in predefined steps
            expect(docStrings).toEqual('I love DocStrings')
        })
    })
    f.Background((b) => {
        b.Given("I've scenarii with same steps", () => {
            f.context.init = true
        })
    })
    // other steps, scenario, etc
})

To finish you can define steps for a rule :

import { defineSteps, loadFeature, describeFeature } from "@amiceli/vitest-cucumber"

const feature = await loadFeature('feature/example.feature')

defineSteps(({ When }) => {
    When('I run unit tests', () => {})
})

describeFeature(feature, (f) => {
    f.Rule('predefined steps in rule', (r) => {
        r.defineSteps(({ Given }) => {
            Given("I don't use docStrings"), () => {
                r.context.initRule = true
            }
        })
        r.Scenario("scenario without docStrings", (s) => {
            s.Then("I use scenario context", () => {
                expect(s.context).toEqual({})
                expect(r.context.initRule).toBe(true)
            })
        })
    })
    // other steps, scenario, etc
})

If you define same step in different scope, it will be overrided :

import { defineSteps, loadFeature, describeFeature } from "@amiceli/vitest-cucumber"

const feature = await loadFeature('feature/example.feature')

defineSteps(({ When }) => {
    When('I run unit tests', () => {})
})

describeFeature(feature, (f) => {
    f.defineSteps(({ When, Given }) => {
        When('I run unit tests', () => {
            // Will override globally defined step
        })
        Given("I don't use docStrings"), () => {})
    })
    f.Rule('predefined steps in rule', (r) => {
        r.defineSteps(({ Given, Then }) => {
            Given("I don't use docStrings"), () => {
                // Will override globally defined in feature
            }
            Then("I use scenario context", () => {})
        })
        r.Scenario("scenario without docStrings", (s) => {
            s.Then("I use scenario context", () => {
                // Will override globally defined in rule
                expect(s.context).toEqual({})
                expect(r.context.initRule).toBe(true)
            })
        })
    })
})

So we can define a formula for defineSteps : global < feature < rule < scenario.

With this feature vitest-cucumber provides steps order. No matter in wish order you define steps with defineSteps and/or-only in Scenario.

It will follow order in your feature file :

Feature: use predefined steps in unit tests
    Background:
        Given I've scenarii with same steps
        And   I use Rule and Feature

You can do:

f.Background((b) => {
    // same in all defineSteps functions
    b.And("I use Rule and Feature", () => {
        expect(b.context.init).toBe(true)
    })
    b.Given("I've scenarii with same steps", () => {
        b.context.init = true
    })
})

Since 6.2.0 predefined step can use expression.. For old version it’s a limit.

They are some limits with context :

  • in globally defined steps you can’t access to Feature, Rule and Scenario context
  • in feature defined steps same for Rule and Scenario context
  • in rule defined step you can’t access to Scenario context