A Flutter package that simplifies Behavior Driven Development (BDD) by automatically generating test files from Gherkin feature files. Write expressive tests in plain English using Given/When/Then scenarios and let BDD Flutter handle the boilerplate code generation.
- Parse
.featurefiles written in Gherkin syntax (Given/When/Then/And) - Generate boilerplate test files automatically
- Support for both widget tests and unit tests
- Incremental generation — new scenarios are appended without overwriting existing implementations
- Instance-based scenario classes for shared state between steps
- Background support for shared setup steps
- Examples tables for parameterized scenarios
- Configurable via
.bdd_flutter/config.yaml - Manifest tracking in
.bdd_flutter/manifest.yaml
Add to your pubspec.yaml:
dev_dependencies:
bdd_flutter: latest- Create a
.featurefile in your test folder:
Feature: Counter
Scenario: Increment
Given I have a counter with value 0
When I increment the counter by <value>
Then the counter should have value <expected_value>
Examples:
| value | expected_value |
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |- Generate test files:
dart run bdd_flutter build-
Implement the generated step methods in
counter.bdd_scenarios.dart -
Run your tests with BDD report:
dart run bdd_flutter testOr run normally with flutter test.
The generator creates two files per .feature file:
.bdd_scenarios.dart— Scenario classes with step method stubs (your implementation goes here).bdd_test.dart— Test orchestration file (auto-generated, do not edit)
- Scenario classes use instance methods, so you can add
latefields for shared state (mocks, widgets, etc.) - Each test instantiates a fresh scenario for proper test isolation
- When you add a new scenario to a
.featurefile, only the new scenario class is appended — existing implementations are preserved - The test file is always regenerated (it contains no user code)
import 'package:flutter_test/flutter_test.dart';
class IncrementScenario {
Future<void> iHaveACounterWithValue0(WidgetTester tester) async {
// TODO: Implement Given I have a counter with value 0
}
Future<void> iIncrementTheCounterByValue(WidgetTester tester, String value) async {
// TODO: Implement When I increment the counter by <value>
}
Future<void> theCounterShouldHaveValueExpectedValue(WidgetTester tester, String expectedValue) async {
// TODO: Implement Then the counter should have value <expected_value>
}
}import 'package:flutter_test/flutter_test.dart';
import 'counter.bdd_scenarios.dart';
void main() {
group('Counter', () {
testWidgets('Increment', (tester) async {
final scenario = IncrementScenario();
final examples = [
{'value': '1', 'expectedValue': '1'},
{'value': '2', 'expectedValue': '2'},
{'value': '3', 'expectedValue': '3'},
];
for (var example in examples) {
await scenario.iIncrementTheCounterByValue(tester, example['value']!);
await scenario.theCounterShouldHaveValueExpectedValue(tester, example['expectedValue']!);
}
});
});
}Configure the generator in .bdd_flutter/config.yaml:
# Where to scan for .feature files
test_dir: "test/"
# Generate widget tests or unit tests
generate_widget_tests: true
# Feature files to skip during generation
ignore_features:
- test/features/login.feature
- test/features/registration.feature
# Imports added to every generated .bdd_scenarios.dart file
additional_imports:
- "package:mocktail/mocktail.dart"
- "test/helpers/test_helpers.dart"
# Suffix for generated scenario class names (default: "Scenario")
# e.g., "Steps" → IncrementSteps instead of IncrementScenario
scenario_suffix: "Scenario"Or use --force to regenerate everything:
dart run bdd_flutter build --force| Option | Type | Default | Description |
|---|---|---|---|
test_dir |
String | test/ |
Directory to scan for .feature files |
generate_widget_tests |
bool | true | Generate widget tests when true, unit tests when false |
ignore_features |
List | [] | Feature file paths to skip during generation |
additional_imports |
List | [] | Imports added to every generated scenario file |
scenario_suffix |
String | Scenario |
Class name suffix (e.g., Steps for IncrementSteps) |
| Command | Description |
|---|---|
build |
Generate test files from .feature files |
build --force |
Force regenerate all files (overwrites existing) |
test |
Run BDD tests with formatted Feature/Scenario report |
Control test type with standard Gherkin tags:
| Decorator | Scope | Description |
|---|---|---|
@unitTest |
Feature, Scenario | Generate unit test (overrides config) |
@widgetTest |
Feature, Scenario | Generate widget test (overrides config) |
Scenario-level decorators override Feature-level ones.
To skip generation for specific features, use ignore_features in config.
dart run bdd_flutter build- Skips unchanged features (tracked via manifest)
- New scenarios are appended to existing scenario files — implementations are preserved
- Test files are regenerated to include all scenarios
dart run bdd_flutter build --force- Regenerates all files from scratch
- Overwrites existing scenario implementations — use with caution
your_project/
├── .bdd_flutter/
│ ├── config.yaml # Configuration (commit to version control)
│ └── manifest.yaml # Generation tracking (commit to version control)
├── test/
│ └── login/
│ ├── login.feature
│ ├── login.bdd_scenarios.dart # Your implementations
│ └── login.bdd_test.dart # Auto-generated orchestration
└── pubspec.yaml
-
Version Control — Keep both
config.yamlandmanifest.yamlin version control. The manifest prevents incremental builds from overwriting implemented code on fresh clones. -
Scenario Files — Implement your test logic in
.bdd_scenarios.dart. Uselatefields for shared state between steps (mocks, providers, widgets). -
Test Files — Do not edit
.bdd_test.dartfiles. They are regenerated automatically and contain no user code. -
Adding Scenarios — When you add new scenarios to a
.featurefile, runbuild— new scenario classes are appended without touching existing ones. -
Feature Files — Keep feature files clean. Only use
@unitTest/@widgetTestdecorators. All other configuration belongs in.bdd_flutter/config.yaml.
This project is licensed under the MIT License - see the LICENSE file for details.