Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/workflows/test_and_format.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Flutter Test and Format

on:
pull_request:
branches: [main]

jobs:
test_and_format:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: "3.x"
channel: "stable"

- name: Install dependencies
run: flutter pub get

- name: Run tests
run: flutter test ./test

- name: Format code
run: |
dart format . --line-length 200 --output=none $(find . -name "*.dart" ! -name "*.g.dart")

- name: Check for changes
run: |
git diff --exit-code -- . ':!example/pubspec.lock' || (echo "::error::Code formatting issues found. Run 'dart format --line-length 200 lib/ test/ example/' to fix." && exit 1)
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"dart.lineLength": 200
}
7 changes: 5 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
## 0.2.0

- remove `@ignore` decorator
- change output files extension to `.bdd_scenarios.dart` and `.bdd_test.dart`
- change output files extension to `.bdd_scenarios.g.dart` and `.bdd_test.g.dart`
- add `ignore_features` option to `build.yaml`
- fix example
- fix import file name
- update readme

## 0.1.5

Expand Down
157 changes: 83 additions & 74 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,34 @@
# BDD Flutter
# 🚀 BDD Flutter

[![pub package](https://img.shields.io/pub/v/bdd_flutter.svg)](https://pub.dev/packages/bdd_flutter)
[![style: very good analysis](https://img.shields.io/badge/style-very_good_analysis-B22C89.svg)](https://pub.dev/packages/very_good_analysis)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Buy Me A Coffee](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/samderlust)

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
## Features

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

## Installation
## 📦 Installation

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

```yaml
dev_dependencies:
bdd_flutter: any
build_runner: any
```

## Usage
## 🚀 Quick Start

Create a `.feature` file in your project:
1. Create a `.feature` file in your project:

```
```gherkin
Feature: Counter
Scenario: Increment
Given I have a counter with value 0
Expand All @@ -36,81 +41,85 @@ Feature: Counter
| 3 | 3 |
```

Run the build command to generate the test files:
2. Generate test files:

```bash
flutter pub run build_runner build
```

This will generate:

- a `sample.scenario.dart` file, containing the scenario implementation.
- a `sample_test.dart` file, containing the test implementation.

then run the test file:
3. Run your tests:

```bash
flutter test
```

## Configuration
## 💡 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

You can configure the test generation by passing options to the builder:
2. **Feature File Ignore**:
Add this comment at the top of your feature file:
```gherkin
@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`:

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

- `generate_widget_tests`: default is true

- if true, generate widget tests
- if false, generate unit tests

- `enable_reporter`: default is false

- if true, enable the reporter for the tests in test file
- if false, disable the reporter for the tests in test file
### Configuration Options

## Decorators
| Option | Type | Default | Description |
| ----------------------- | ---- | ------- | ------------------------------------------------------ |
| `generate_widget_tests` | bool | true | Generate widget tests when true, unit tests when false |
| `enable_reporter` | bool | false | Enable/disable test reporter |
| `ignore_features` | List | [] | List of feature file paths to ignore during generation |

You can use the following decorators to control the test generation:
## 🏷️ Decorators

- `@unitTest`:
- Generate a unit test (ignore build.yaml config)
- apply to feature, scenario
- `@widgetTest`:
- Generate a widget test (ignore build.yaml config)
- apply to feature, scenario
- `@className`:
- Generate a custom class name for the scenario
- apply to scenario
- example: `@className("CustomName")`
- `@enableReporter`:
- Enable the reporter for the tests in the feature
- apply to feature
- `@disableReporter`:
- Disable the reporter for the tests in the feature
- apply to feature
Control test generation with decorators:

decorators can be used at the feature, scenario level. The lower the level, the more specific the decorator and it will override the upper level decorator.
| Decorator | Scope | Description |
| -------------------------- | ----------------- | --------------------------------------- |
| `@unitTest` | Feature, Scenario | Generate unit test (overrides config) |
| `@widgetTest` | Feature, Scenario | Generate widget test (overrides config) |
| `@className("CustomName")` | Scenario | Generate custom class name |
| `@enableReporter` | Feature | Enable test reporter |
| `@disableReporter` | Feature | Disable test reporter |

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

Contributions are welcome! Please open an issue or submit a pull request.
## 📝 Complete Example

## License

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

## Example

1. create a feature file (e.g. `counter.feature`)
### 1. Feature File (`counter.feature`)

```gherkin
Feature: Counter
Expand All @@ -125,15 +134,9 @@ Feature: Counter
| 3 | 3 |
```

2. run the builder

```bash
flutter pub run build_runner build
```

3. generated file
### 2. Generated Files

- `counter_scenarios.dart`
#### `counter_scenarios.dart`

```dart
import 'package:flutter_test/flutter_test.dart';
Expand All @@ -143,17 +146,17 @@ class IncrementScenario {
// TODO: Implement Given I have a counter with value 0
}

static Future<void> iIncrementTheCounterBy(WidgetTester tester,dynamic value) async {
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 {
static Future<void> theCounterShouldHaveValue(WidgetTester tester, dynamic expected_value) async {
// TODO: Implement Then the counter should have value <expected_value>
}
}
```

- `counter_test.dart`
#### `counter_test.dart`

```dart
import 'package:flutter_test/flutter_test.dart';
Expand All @@ -177,8 +180,14 @@ void main() {
}
```

4. run the test
## 🤝 Contributing

```bash
flutter test
```
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](LICENSE) file for details.
3 changes: 2 additions & 1 deletion build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ builders:
bdd_flutter|bdd_test_builder:
import: "package:bdd_flutter/bdd_flutter.dart"
builder_factories: ["bddTestBuilder"]
build_extensions: { ".feature": [".bdd_scenarios.dart", ".bdd_test.dart"] }
build_extensions:
{ ".feature": [".bdd_scenarios.g.dart", ".bdd_test.g.dart"] }
auto_apply: dependents
build_to: source
3 changes: 3 additions & 0 deletions example/bdd_ignore.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ignore_files:
- test/sample/sample.bdd_scenarios.dart
- test/sample/sample.bdd_test.dart
2 changes: 2 additions & 0 deletions example/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ targets:
options:
# generate_widget_tests:
# enable_reporter: true
ignore_features:
- "test/calculator/calculator.feature"
4 changes: 1 addition & 3 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ class MainApp extends StatelessWidget {

@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(body: Center(child: Text('Hello World!'))),
);
return const MaterialApp(home: Scaffold(body: Center(child: Text('Hello World!'))));
}
}
2 changes: 1 addition & 1 deletion example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ packages:
path: ".."
relative: true
source: path
version: "0.1.5"
version: "0.2.0"
boolean_selector:
dependency: transitive
description:
Expand Down
5 changes: 0 additions & 5 deletions example/test/calculator/calculator.bdd_scenarios.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ class AddTwoNumbersScenario {
static Future<void> theResultShouldBe3(WidgetTester tester) async {
// TODO: Implement Then the result should be 3
}

}

class SubtractTwoNumbersScenario {
Expand All @@ -27,7 +26,6 @@ class SubtractTwoNumbersScenario {
static Future<void> theResultShouldBe2(WidgetTester tester) async {
// TODO: Implement Then the result should be 2
}

}

class MultiplyTwoNumbersScenario {
Expand All @@ -42,7 +40,6 @@ class MultiplyTwoNumbersScenario {
static Future<void> theResultShouldBe6(WidgetTester tester) async {
// TODO: Implement Then the result should be 6
}

}

class DivideTwoNumbersScenario {
Expand All @@ -57,6 +54,4 @@ class DivideTwoNumbersScenario {
static Future<void> theResultShouldBe(WidgetTester tester, String result) async {
// TODO: Implement Then the result should be <result>
}

}

18 changes: 9 additions & 9 deletions example/test/calculator/calculator.bdd_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,17 @@ void main() {
testWidgets('Divide two numbers', (tester) async {
//Scenario: Divide two numbers
final examples = [
{'number1': '10','number2': '2','result': '5',},
{'number1': '10','number2': '1','result': '10',},
{'number1': '10','number2': '10','result': '1',},
{'number1': '10', 'number2': '2', 'result': '5'},
{'number1': '10', 'number2': '1', 'result': '10'},
{'number1': '10', 'number2': '10', 'result': '1'},
];
for (var example in examples) {
// Given I have the number <number1>
await DivideTwoNumbersScenario.iHaveTheNumber(tester, example['number1']!);
// When I divide them
await DivideTwoNumbersScenario.iDivideThem(tester);
// Then the result should be <result>
await DivideTwoNumbersScenario.theResultShouldBe(tester, example['result']!);
// Given I have the number <number1>
await DivideTwoNumbersScenario.iHaveTheNumber(tester, example['number1']!);
// When I divide them
await DivideTwoNumbersScenario.iDivideThem(tester);
// Then the result should be <result>
await DivideTwoNumbersScenario.theResultShouldBe(tester, example['result']!);
}
});
});
Expand Down
1 change: 0 additions & 1 deletion example/test/calculator/calculator.feature
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
@ignore
Feature: Calculator

Scenario: Add two numbers
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'counter.bdd_scenarios.dart';
import 'counter.bdd_scenarios.g.dart';

void main() {
group('Counter', () {
Expand Down
Loading