bdd_flutter 0.2.0

Platformwindowslinuxmacos

A powerful Flutter package that simplifies Behavior Driven Development (BDD) by automatically generating test files from Gherkin feature files

🚀 BDD Flutter

pub package style: very good analysis License: MIT Buy Me A Coffee

A powerful 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.

✨ Features

  • 📝 Parse .feature files written in Gherkin syntax
  • ⚡ Generate boilerplate test files automatically
  • 🧪 Support for both widget tests and unit tests
  • ⚙️ Configurable test generation

📦 Installation

Add the following dependencies to your package's pubspec.yaml file:

dev_dependencies:
  bdd_flutter: any
  build_runner: any

🚀 Quick Start

  1. Create a .feature file in your project:
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              |
  1. Generate test files:
flutter pub run build_runner build
  1. Run your tests:
flutter test

💡 Recommendations

When working with generated test files, follow these best practices:

  1. Generated Files:

    • Generated files will have the .g.dart extension (e.g., counter_test.bdd_test.g.dart or counter_scenarios.g.dart)
    • After implementing your tests, it's recommended to:
      • Remove the .g extension from the file name
      • Add an ignore decorator to your feature file (@ignore)
    • This will prevent the generated files from being overwritten by subsequent builds
  2. Feature File Ignore: Add this comment at the top of your feature file:

    @ignore
    Feature: Counter
    

This approach ensures that:

  • Your implemented tests won't be overwritten by subsequent builds
  • Generated files are properly ignored in version control
  • You maintain a clean project structure

⚙️ Configuration

Configure test generation in your build.yaml:

targets:
  $default:
    builders:
      bdd_flutter|bdd_test_builder:
        options:
          generate_widget_tests: false # Default: true
          enable_reporter: true # Default: false
          ignore_features:
            - "test/features/ignored.feature"
            - "test/features/another_ignored.feature"

Configuration Options

OptionTypeDefaultDescription
generate_widget_testsbooltrueGenerate widget tests when true, unit tests when false
enable_reporterboolfalseEnable/disable test reporter
ignore_featuresList[]List of feature file paths to ignore during generation

🏷️ Decorators

Control test generation with decorators:

DecoratorScopeDescription
@unitTestFeature, ScenarioGenerate unit test (overrides config)
@widgetTestFeature, ScenarioGenerate widget test (overrides config)
@className("CustomName")ScenarioGenerate custom class name
@enableReporterFeatureEnable test reporter
@disableReporterFeatureDisable test reporter

💡 Decorators follow a hierarchy: Scenario-level decorators override Feature-level ones.

📝 Complete Example

1. Feature File (counter.feature)

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              |

2. Generated Files

counter_scenarios.dart

import 'package:flutter_test/flutter_test.dart';

class IncrementScenario {
  static Future<void> iHaveACounterWithValue0(WidgetTester tester) async {
    // TODO: Implement Given I have a counter with value 0
  }

  static Future<void> iIncrementTheCounterBy(WidgetTester tester, dynamic value) async {
    // TODO: Implement When I increment the counter by <value>
  }

  static Future<void> theCounterShouldHaveValue(WidgetTester tester, dynamic expected_value) async {
    // TODO: Implement Then the counter should have value <expected_value>
  }
}

counter_test.dart

import 'package:flutter_test/flutter_test.dart';
import 'counter_scenarios.dart';

void main() {
  group('Counter', () {
    testWidgets('Increment', (tester) async {
      await IncrementScenario.iHaveACounterWithValue0(tester);
      // Example with values: 1, 1
      await IncrementScenario.iIncrementTheCounterBy(tester, '1');
      await IncrementScenario.theCounterShouldHaveValue(tester, '1');
      // Example with values: 2, 2
      await IncrementScenario.iIncrementTheCounterBy(tester, '2');
      await IncrementScenario.theCounterShouldHaveValue(tester, '2');
      // Example with values: 3, 3
      await IncrementScenario.iIncrementTheCounterBy(tester, '3');
      await IncrementScenario.theCounterShouldHaveValue(tester, '3');
    });
  });
}

🤝 Contributing

We welcome contributions! Please feel free to:

  • Open an issue
  • Submit a pull request
  • Share your feedback

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.