đŸĨ’ Pickled cucumber đŸĨ’

A simple Dart engine for Cucumber scenarios.

Works with đŸŽ¯ Dart, 🐸 Dart Frog and đŸĻ Flutter projects.

ℹī¸ Note:

This is a non official package, not affiliated with the Cucumber nor the Dart project.

The goal of this project is to provide a simple way to run Cucumber scenarios in Dart and Flutter projects.

This project does not aim to be a full Cucumber implementation. It only supports the most common features of Cucumber.

Table of contents

Getting started

Add it to your pubspec.yaml:

dart pub add pickled_cucumber

or add it manually:

dependencies:
  pickled_cucumber: ^1.0.0 # use the latest version

Usage

Dart usage

Write your first feature file in any directory

# features/counter.feature

Feature: Counter

  Scenario: Increment counter
    Given a variable set to 1
    When I increment the variable by 1
    Then the variable should contain 2

Create your Dart step definitions file

// features/step_definitions/counter_steps.dart
class CounterStepDefs {

    int _counter;
    
    @Given("a variable set to {int}")
    void aVariableIsSetTo(int value) {
        _counter = value;
    }
    
    @When("I increment the variable by {int}")
    void iIncrementTheVariableBy(int value) {
        _counter += value;
    }

    @Then("the variable should contain {int}")
    void theVariableShouldContain(int value) {
        expect(_counter, value);
    }
}

Create your entry point in test directory

// test/cucumber_test.dart
import 'package:pickled_cucumber/pickled_cucumber.dart';
import 'package:counter/features/step_definitions/counter_steps.dart';

void main() {
    final stepDefsDartFile = CounterStepDefs();

    PickledCucumber.runFeatures(
        "features/counter.feature",
        stepDefsDartFile,
    );
}

Run your tests

dart test test/cucumber_test.dart

Dart Frog usage

Pickled cucumber works the exact same way as Dart with Dart Frog.

Write your first feature file in any directory

# test/features/index.feature
Feature: index

    Scenario: GET index route
        Given my app is running
        When I visit the index route
        Then I should see "Welcome to Dart Frog!"
        And receive a 200 status code

Create your Dart Frog step definitions file

// test/step_definitions.dart
import 'package:pickled_cucumber/pickled_cucumber.dart';
import 'package:dart_frog/dart_frog.dart';
import 'package:mocktail/mocktail.dart' as mocktail;
import 'package:test/test.dart';
import '../routes/index.dart' as route;

class MockRequestContext extends mocktail.Mock implements RequestContext {}

class DartFrogStepDefinition {
  late MockRequestContext context;
  Response? response;

  @Given('my app is running')
  void myAppIsRunning() {
    context = MockRequestContext();
  }

  @When('I visit the index route')
  void iVisitTheIndexRoute() {
    response = route.onRequest(context);
  }

  @Then('I should see {string}')
  Future<void> iShouldSee(String string) async {
    expect(response!.body(), completion(equals(string)));
  }

  @And('receive a {int} status code')
  void receiveStatusCode(int statusCode) {
    expect(response!.statusCode, equals(statusCode));
  }
}

Create your entry point in test directory

// test/cucumber_test.dart
import 'package:pickled_cucumber/pickled_cucumber.dart';

import 'step_definitions.dart';

void main() {
  PickledCucumber().runFeatures(
    'test/features/',
    DartFrogStepDefinition(),
  );
}

Flutter usage

With Pickled cucumber, you can make Flutter integration tests and Widget tests using Cucumber scenarios.

Pickled cucumber works with code generation for Flutter projects.

Please add build_runner dependency to your project

flutter pub add dev:build_runner

Write your first feature file in test/features directory

# test/features/counter.feature

Feature: counter
    counter should increment

    Scenario: increment counter
      Given counter is 0
      When I increment counter
      Then counter should be 1

Create your Dart step definitions file in test/ directory

⚠ī¸ IMPORTANT: only import annotations ⚠ī¸

// test/counter_steps.dart

// IMPORTANT: ⚠ī¸ only import annotations ⚠ī¸
import 'package:pickled_cucumber/src/annotations.dart';
import 'package:flutter/material.dart';
import 'package:flutter_example/main.dart';
import 'package:flutter_test/flutter_test.dart';

@StepDefinition()
class CounterSteps {

  @Given('counter is {int}')
  Future<void> counterIs(WidgetTester tester, int counter) async {
    debugPrint('counter is $counter');
    await tester.pumpWidget(const MyApp());

    expect(find.text('$counter'), findsOneWidget);
  }

  @When('I increment counter')
  Future<void> iIncrementCounter(
    WidgetTester tester,
  ) async {
    debugPrint('I increment counter');

    await tester.tap(find.byIcon(Icons.add));
    await tester.pumpAndSettle();
  }

  @Then('counter should be {int}')
  Future<void> counterShouldBe(WidgetTester tester, int counter) async {
    debugPrint('counter should be $counter');

    expect(find.text('$counter'), findsOneWidget);
  }
}

Run build_runner to generate the step definitions file

dart run build_runner build --delete-conflicting-outputs

Create your entry point in test directory

// test/cucumber_test.dart
import 'counter_steps.pickled.dart';

main() => runFeatures();

Run your tests as Widget tests

flutter test test/cucumber_test.dart
# or
flutter test

Run your tests as Integration tests

Create your entry point in integration_test directory

// integration_test/app_test.dart
import 'package:integration_test/integration_test.dart';

import '../test/counter_step_definitions.g.dart';

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  runFeatures();
}

Run your tests

flutter test integration_test/app_test.dart

Libraries

builder
pickled_cucumber
Pickled cucumber library