GithubHelp home page GithubHelp logo

olexale / bdd_widget_test Goto Github PK

View Code? Open in Web Editor NEW
96.0 96.0 31.0 447 KB

A BDD-style widget testing library

License: MIT License

Dart 93.54% Gherkin 1.55% Kotlin 0.08% Ruby 1.69% Swift 0.79% Objective-C 0.02% HTML 2.33%

bdd_widget_test's Introduction

Stand With Ukraine

Hi, my name is Oleksandr πŸ‘‹

I'm a cross-platform mobile developer from Ukraine πŸ‡ΊπŸ‡¦

A few facts about me:

  • 🐦 I'm a Google Developer Expert in Flutter and Dart
  • πŸ“± I started developing mobile apps in times when iPhones had non-retina displays, Android was ugly, and Windows Phone was incredibly cool
  • πŸ§ͺ I'm passionate about software quality and tests
  • 🎀 I like to share the knowledge
  • 🎸 I played bass guitar in a jazz band

bdd_widget_test's People

Contributors

daniel-deboeverie-lemon avatar eikebartels avatar foxanna avatar kentcb avatar lsaudon avatar mkhtradm01 avatar olexale avatar ron-brosh avatar vidibu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bdd_widget_test's Issues

Getting group and testWidgets description in step

Hi,
is there a way to get group and testWidgets descriptions in test steps? I want to display them as overlay in app. I was thinking about something like that but it seems like breaking change.

class TestInfo {
  TestInfo(
    this.groupDescription,
    this.testWidgetsDescription,
  );

  final String groupDescription;
  final String testWidgetsDescription;
}

Future<void> stepName(
  WidgetTester tester,
  TestInfo testInfo,
  dynamic param1,
) async {
  //step body
}

[Proposal] Add a comment above the created step.

This would make it easy to find step in dart code.

A file with this

    Scenario Outline: Plus button increases the counter
        Given the app is running

Becomes that.

import 'package:flutter_test/flutter_test.dart';
import 'package:dummy_yaml/main.dart';

/// the app is running
Future<void> theAppIsRunning(WidgetTester tester) async {
  final widget = MyApp();
  await tester.pumpWidget(widget);
}

Broken comments

In 1.7.1 we have broken comments.

Steps to reproduce

Feature: Counter
    Here is a comment
    Background:
        Given the app is running
    
    After:
        # Just for the demo purpose, you may write "I don't see {'surprise'} text" to use built-in step instead.
        # See the list of built-in step below.
        And I do not see {'surprise'} text 
    
    # @testMethodName: testGoldens
    Scenario: Initial counter value is 0
        Then I see {'0'} text
   ....

generates:

// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: unused_import, directives_ordering

Here is a comment

# Just for the demo purpose, you may write "I don't see {'surprise'} text" to use built-in step instead.
# See the list of built-in step below.

# @testMethodName: testGoldens

# Scenario: Built-in steps
#     And I don't see {Icons.add} icon
#     And I don't see {'text'} rich text
#     And I don't see {'text'} text
#     And I don't see {Container} widget
#     And I enter {'text'} into {1} input field
#     And I see disabled elevated button
#     And I see enabled elevated button
#     And I see exactly {4} {Container} widgets
#     And I see {Icons.add} icon
#     And I see multiple {'text'} texts
#     And I see multiple {Container} widgets
#     And I see {'text'} rich text
#     And I see {'text'} text
#     And I tap {Icons.add} icon
#     And I wait
#     And I dismiss the page
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

import './step/common/the_app_is_running.dart';
import './step/i_do_not_see_text.dart';
import 'package:bdd_widget_test/step/i_see_text.dart';
import 'package:bdd_widget_test/step/i_tap_icon.dart';
import './step/i_tap_icon_times.dart';

void main() {
  group('''Counter''', () {
...

Expected behavior

The generated code should be

// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: unused_import, directives_ordering

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

import './step/common/the_app_is_running.dart';
import './step/i_do_not_see_text.dart';
import 'package:bdd_widget_test/step/i_see_text.dart';
import 'package:bdd_widget_test/step/i_tap_icon.dart';
import './step/i_tap_icon_times.dart';

void main() {
  group('''Counter''', () {
    Future<void> bddSetUp(WidgetTester tester) async {
      await theAppIsRunning(tester);
    }
    Future<void> bddTearDown(WidgetTester tester) async {
...

Error when custom step folder in integration tests

First of all thanks for this great library.

I have an error when I set a custom folder for the generated steps using integration tests (I'm using the last version 1.3.0):

This is my build.yaml:
targets: $default: sources: - integration_test/** # By default, build runner will not generate code in the integration folder - test/** # so we override paths for code generation here - lib/** - $package$ builders: bdd_widget_test|featureBuilder: options: include: package:bdd_widget_test/bdd_options.yaml stepFolderName: ../../my_steps

And this is the output when I lunch the test:
org-dartlang-app:/survey_login_test.dart:12:8: Error: Error when reading 'org-dartlang-app:/my_steps/i_see_complex_text.dart': File not found import './../../my_steps/i_see_complex_text.dart';

If I remove the stepFolderName the test is working like a charm.

Thanks in advance

support for build ^2.0.0

I love this tool, but currently it is referencing build ^1.6.1. A lot of packages I use reverence build ^2.0.0 and it would be helpful if this package references it as well.

[Proposal] Add Hooks and remove After: because don't exist in Gherkin syntax

I think it's possible to add the notion of hooks to the package.
Also, After: doesn't exist in Gherkin syntax.

Suggestion

Generate a Hooks class like steps.

hooks.dart

class Hooks {
  static Function before = () {
    // Add logic for before
  };
  static Function beforeStep = () {
    // Add logic for beforeStep
  };
  static Function afterStep = () {
    // Add logic for afterStep
  };
  static Function after = () {
    // Add logic for after
  };
}

sample_test.dart

void main() {
  group('''Counter''', () {
    setUp(() {
      Hooks.before.call();
    });

    tearDown(() {
      Hooks.after.call();
    });

    testWidgets('''Scenario One''', (tester) async {
        Hooks.beforeStep.call();
        await iSeeText(tester, '0');
        Hooks.afterStep.call();
        Hooks.beforeStep.call();
        await iSeeText(tester, '1');
        Hooks.afterStep.call();
    });
  });
}

If we can do this.
After: has no reason to exist.

Feature: Use Background to avoid repeating the same steps in the subsequent scenarios

I have a question related to the Background when you don't want to repeat the same statements at the beginning of each scenario

Example

Feature: Counter

  Scenario: starts with initial value of 0
    Given I visit the {'/counters'} page
    Then I see {"0"}


  Scenario:  displays 'Trick!' for the multiple of  3
    Given I visit the {'/counters'} page
    When I tap {Icons.add} icon {6} times
    Then I see {"6"}
    And I see {"Trick!"}
  #

Would it be possible to simply add a background?

Feature: Counter

  Background:
     Given I visit the counter page

  Scenario: starts with initial value of 0
    Given I visit the {'/counters'} page
    Then I see {"0"}


  Scenario:  displays 'Trick!' for the multiple of 3
    Given I visit the {'/counters'} page
    When I tap {Icons.add} icon {6} times
    Then I see {"6"}
    And I see {"Trick!"}
  #

Fix coverage

Need to cover the generator itself with unit tests.

  • Investigate build_test
  • Create an abstraction for dart:io and dart:isolate entities

Feature tags misplaced

Tags from feature are placed between general imports and local imports. this prevent tags to be used to filter tests.
Example feature file :

@admin
Feature: feature admin

Rule: Wrong usage of the app

    Background: 
        Given The application is running
    @Id-59313
    @run_type_Manual
    Scenario: Wrong way to use the app
        When I do a wrong step
        Then I should have the wrong result

Resulting dart test file :

// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: unused_import, directives_ordering

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

@Tags(['admin'])
import './../../bdd_steps,/the_application_is_running.dart';
import './../../bdd_steps,/i_do_a_wrong_step.dart';
import './../../bdd_steps,/i_should_have_the_wrong_result.dart';

void main() {
  Future<void> bddSetUp(WidgetTester tester) async {
    await theApplicationIsRunning(tester);
  }
  group('''feature admin''', () {
    testWidgets('''Wrong way to use the app''', (tester) async {
      await bddSetUp(tester);
      await iDoAWrongStep(tester);
      await iShouldHaveTheWrongResult(tester);
    }, tags: ['Id-59313', 'run_type_Manual']);
  });
}

@Tags(['admin']) should be placed before any imports

Slow tag not working with Golden tests

When tagging a golden test with the keyword "slow", the test will still run, even when excluded.

It seems that a tag is only taken into account if it is at the very beginning of the file. But when I insert the tag above the import for golden tests, then the import is not added. But if I insert the tag after the import, then the tag is ignored

import 'package:golden_toolkit/golden_toolkit.dart';

@slow
@testMethodName: testGoldens
Feature: My golden test
...

Generates:

import 'package:golden_toolkit/golden_toolkit.dart';

@Tags(['slow'])
import 'package:flutter/material.dart';

...

While:

@slow
import 'package:golden_toolkit/golden_toolkit.dart';

@testMethodName: testGoldens
Feature: My golden test

...

Generates:

@Tags(['slow'])
import 'package:flutter/material.dart';

...

This bug issue can also be the opportunity for a feature request :D

It has always bugged me that the imports occasionally have to be inserted into the .feature files. My suggestion would be to add another tag called "imports". If this tag is present, an imports.dart file could simply be created and imported into which all imports could be inserted, like a barrel file.

@slow
@imports
@testMethodName: testGoldens
Feature: My golden test

...

Would generate:

@Tags(['slow'])
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import './../imports.dart';

....

And the import.dart, could simply be expanded with:

export 'package:golden_toolkit/golden_toolkit.dart';

The path and name of the file could perhaps be specified in the build.yaml.

Could this be a suitable solution?

[Proposal] Put tests in a function so they can be called from `integration_test`

Put tests in a function so they can be called from integration_test

Before

// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: unused_import, directives_ordering

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

import './step/common/the_app_is_running.dart';
import './step/i_do_not_see_text.dart';
import 'package:bdd_widget_test/step/i_see_text.dart';
import 'package:bdd_widget_test/step/i_tap_icon.dart';
import './step/i_tap_icon_times.dart';

void main() {
  group('''Counter''', () {
    Future<void> bddSetUp(WidgetTester tester) async {
      await theAppIsRunning(tester);
    }
    Future<void> bddTearDown(WidgetTester tester) async {
      await iDoNotSeeText(tester, 'surprise');
    }
    testWidgets('''Initial counter value is 0''', (tester) async {
      try {
        await bddSetUp(tester);
        await iSeeText(tester, '0');
      } finally {
        await bddTearDown(tester);
      }
    });
    testWidgets('''Add button increments the counter''', (tester) async {
      try {
        await bddSetUp(tester);
        await iTapIcon(tester, Icons.add);
        await iSeeText(tester, '1');
      } finally {
        await bddTearDown(tester);
      }
    });
    testWidgets('''Outline: Plus button increases the counter (0, '0')''', (tester) async {
      try {
        await bddSetUp(tester);
        await theAppIsRunning(tester);
        await iTapIconTimes(tester, Icons.add, 0);
        await iSeeText(tester, '0');
      } finally {
        await bddTearDown(tester);
      }
    });
    testWidgets('''Outline: Plus button increases the counter (1, '1')''', (tester) async {
      try {
        await bddSetUp(tester);
        await theAppIsRunning(tester);
        await iTapIconTimes(tester, Icons.add, 1);
        await iSeeText(tester, '1');
      } finally {
        await bddTearDown(tester);
      }
    });
    testWidgets('''Outline: Plus button increases the counter (42, '42')''', (tester) async {
      try {
        await bddSetUp(tester);
        await theAppIsRunning(tester);
        await iTapIconTimes(tester, Icons.add, 42);
        await iSeeText(tester, '42');
      } finally {
        await bddTearDown(tester);
      }
    });
  });
}

After

// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: unused_import, directives_ordering

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

import './step/common/the_app_is_running.dart';
import './step/i_do_not_see_text.dart';
import 'package:bdd_widget_test/step/i_see_text.dart';
import 'package:bdd_widget_test/step/i_tap_icon.dart';
import './step/i_tap_icon_times.dart';

void main() {
  counterFeature();
}

void counterFeature() {
  group('''Counter''', () {
    Future<void> bddSetUp(WidgetTester tester) async {
      await theAppIsRunning(tester);
    }

    Future<void> bddTearDown(WidgetTester tester) async {
      await iDoNotSeeText(tester, 'surprise');
    }

    testWidgets('''Initial counter value is 0''', (tester) async {
      try {
        await bddSetUp(tester);
        await iSeeText(tester, '0');
      } finally {
        await bddTearDown(tester);
      }
    });
    testWidgets('''Add button increments the counter''', (tester) async {
      try {
        await bddSetUp(tester);
        await iTapIcon(tester, Icons.add);
        await iSeeText(tester, '1');
      } finally {
        await bddTearDown(tester);
      }
    });
    testWidgets('''Outline: Plus button increases the counter (0, '0')''',
        (tester) async {
      try {
        await bddSetUp(tester);
        await theAppIsRunning(tester);
        await iTapIconTimes(tester, Icons.add, 0);
        await iSeeText(tester, '0');
      } finally {
        await bddTearDown(tester);
      }
    });
    testWidgets('''Outline: Plus button increases the counter (1, '1')''',
        (tester) async {
      try {
        await bddSetUp(tester);
        await theAppIsRunning(tester);
        await iTapIconTimes(tester, Icons.add, 1);
        await iSeeText(tester, '1');
      } finally {
        await bddTearDown(tester);
      }
    });
    testWidgets('''Outline: Plus button increases the counter (42, '42')''',
        (tester) async {
      try {
        await bddSetUp(tester);
        await theAppIsRunning(tester);
        await iTapIconTimes(tester, Icons.add, 42);
        await iSeeText(tester, '42');
      } finally {
        await bddTearDown(tester);
      }
    });
  });
}

And in integration_test :

import 'package:integration_test/integration_test.dart';

import '../test/sample_test.dart';

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();
  counterFeature();
}

How to avoid code formatting for generated test files?

Hi,

In my CI workflow, the first step I have added is code formatting using the command dart format -o none --set-exit-if-changed ., which stops the CI process if the code is not formatted correctly. However, the CI always fails because the generated test files are not formatted.

Is it possible to add an option to postfix the generated files with .g.dart so I can exclude them from being committed? Alternatively, how do you suggest I solve this issue?

code format

Add possibillity to skip tests

Hi,

it would be great if there was a possibillity to somehow skip certain tests.
Our workflow is like this at the moment:

  • We start to work on a feature by creating a gherkin test, that automatically tests our acceptance tests
  • We implement the feature and the steps behind a feature flag
  • We know we are done when the tests are green
  • We then switch the feature flag

This means, our software is theoretically releasable, even though the WIP gherkin tests are not green yet.

We would like to be able to mark a test as WIP, so we can skip it in our CI/CD pipeline.

Custom `stepFolderName` Generates Files Only in `test` Folder

When specifying a custom stepFolderName in build.yaml, the generated dart files are unexpectedly created in the test folder instead of the intended directory (where the feature files belong, e.g integration_test). Is it possible when set the stepFolderName it will generate to the integration_test?

Thanks in advanced!

Seperate tests in folders

Greetings,

It would be ideal for us to have a folder structure to separate tests e.g.

test/
test/step/
test/bdd/
test/bdd/epic-name/test_name.feature

which would then generate the test/bdd/epic_name/test_name_test.dart but referencing the shared steps at the test/step/ folder?

Could this be configured?

Config file

It would be nice to have some sort of configuration file to share settings between various packages in large projects. It can be something like bdd_config.yaml with the content:

include: package:some_common_package/bdd_config.yaml

stepFolderName: ../test/step # this trick is required to share steps between widget and integration tests
# testMethodName: customTestMethodName
externalSteps:
  - package:bdd_widget_test/step/i_see_text.dart
  - package:bdd_widget_test/step/i_dont_see_text.dart
...

Change void main to Future<void>

I don't want to run each test file one at the time cause it take forever and rebuild everything each time.

So what I'm used to do is to load each main into a master main and launch them sequentially like:

void main() {
  await main1();
  await main2();
  ....
}

Currently that's not doable here. Is this something you could add? Or is there another way to do it ?

Invalid code generated on Windows

Thanks for this library: I'm hoping I can use it in my current project rather than a home-grown solution I have (which is fine apart from not having any codegen).

I've noticed an issue with the generated dart file for a feature: it uses relative imports separated by the file system's separator (/ on Linux/Mac, \ on Windows):

import '.\step\the_app_is_running.dart';
import '.\step\the_answer_is.dart';
import '.\step\i_see_text.dart';
import '.\step\i_tap_icon.dart';

However, this results in errors on Windows because it's treating them as escape sequences:

image

If I change them all to / it works fine.

I'd also suggest generating this file with a .g.dart extension so that it's more clearly identified as generated code.

Cannot share steps between feature files with different hierarchy level

In our project we would like to organize our feature files like this:

  • Category 1
    • Sub-Category 1
      • Feature 1.1
    • Sub-Category 2
      • Feature 1.2
    • Feature 1
  • Category 2
    • Feature 2
    • Feature 3

Since feature files does not share the same level hierarchically, multiple step folders are created even with a relative stepFolderName.
Result :

  • Category 1
    • step folder
    • Sub-Category 1
      • step folder
      • Feature 1.1
    • Sub-Category 2
      • step folder
      • Feature 1.2
    • Feature 1
  • Category 2
    • step folder
    • Feature 2
    • Feature 3

Can we have an option, so we can use a path relative to the project folder to generate steps independently of the feature files hierarchy ?

Code generation

Comments will be included as lines of code in the file generated for running tests if the .feature file starts with those comments.

Add a "Background" step for all generated tests

All my tests begin with a "Given prelude" step, setting up GetIt, HttpMockAdapter, default values for Mocks, SharedPrefs, etc. So all my Features or each Scenario have to start with "Background: Given prelude". Is there a way to predefine steps for all generated tests?

Support integration_test

can this package support integration_test as well widget test, because I think the same code can use for the integration test just need to add the following line? ad the beginning of the main method

  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

and also the output folder to integration_test

Bug with Background and support for "tear-down"?

Hi,

Flutter apps that include timers need to ensure those timers are stopped before the widget test completes, otherwise you get the dreaded:

image

For now, I am working around this by including this final step in my scenarios:

Then the application is torn down

In my case, this involves dispatching a TearDown Redux action so that, for example, middleware that has started a periodic timer can stop that timer.

But the problem is now all my scenarios need to end with that step, and it won't make a lot of sense to a business user reading the specification.

It would be nice if I could specify some behavior to execute at the end (and start?) of every scenario. I thought that Background was this exact thing for adding steps that execute at the start of every scenario, but it instead generates code inside setUp which does not have access to the WidgetTester (and therefore fails to even compile). I assume this is a bug?

Anyway, if the steps in Background were actually inserted within the testWidgets call (which I think was the intent judging by the docs), then should there be a counterpart to Background that executes at the end of every scenario? That would solve my problem.

How to call setupAll

Hey @olexale

This is very cool plugins and very easy to use.

If you have a time. Can you provide example how to call setupAll method with your plugins.

Suggestion for Inclusion of BDD Testing Topic Links in Official Flutter Documentation

As we're all aware, Flutter testing is a vast subject encompassing various methodologies and approaches. However, there seems to be a gap in coverage when it comes to Behavior-Driven Development (BDD) testing and its associated packages.

For newcomers to Flutter, especially those who are learning from the official Flutter documentation on flutter.dev, the absence of information on BDD testing could be a significant hurdle in their learning journey.

Hence, I would like to propose the addition of BDD testing topics and relevant package links to the official Flutter documentation. Similar to how state management options are detailed, providing resources on BDD testing would greatly enhance the comprehensiveness of the documentation.

From personal experience and interactions within the developer community, it appears that many developers are unaware of the existence and benefits of BDD testing in the Flutter ecosystem. Personally,

I only discovered this aspect through discussions on architecture, such as your insightful Lego architecture talk

By incorporating BDD testing into the official Flutter documentation, we can ensure that developers have access to comprehensive resources right from the outset of their learning journey, thereby fostering better testing practices within the Flutter community.

Mapping of the step in the .feature file to its step definition

Hi Olex,

Thanks a lot for this very useful package. We are currently applying it in our flutter project for e2e testing solution(automation of the regression test suite) and we find it very useful and without an alternative. The only thing that we miss for now is the mapping of the step in the .feature file to its step definition (command+click on the step in the .feature file opens the step definition). It brings readability, maintainability and is more useful in the reports. Our question is - is it doable in a fork, in your opinion?

Thanks a lot in advance,
All the best,
Iliyan

label:enhancement Supporting diacritics (accents)

Given that scenarios can be written in languages other than english, I think it would be great to take into account diactrics (accents) when generating the functions that correspond to steps.

Currently, the following step "Given los diacrΓ­ticos son ΓΊtil" corresponds to a function called losDiacrticosSonTil. Sometimes, the meaning of the step is lost if a lot of diacritics are completly ignored.

My suggestion is to remove simply diacritcs and keep the letters (é to e, č to c...) when generating the functions and files corresponding to the steps with the build_runner.

The following links can be helpful https://stackoverflow.com/a/65461381 and https://pub.dev/packages/diacritic.

World within scenario

I wish to add something like the world in cucumber.js (https://github.com/cucumber/cucumber-js/blob/main/docs/support_files/world.md)

This would allow for state to pass between steps within a scenario.

I would recommend the addition of a flag that either enables or disables this. In the scenario you then create an object that contains a Map<String, dynamic> that is passed along in the scenarios.

By wrapping this map in an object it will be able to expand it later (with for example logs like the cucumber.js has).
I think of using a key value map since this seems the most flexible for sharing whatever state you need.

Pattern for per-scenario state?

Hi,

Is there any support for passing state through a feature/scenario? I have abstracted all integration with the real world into APIs and have mocks of those APIs, but it's unclear how to best:

  1. Create an instance of those mocks when a scenario starts. I can do this easily enough as a step, but what does the step then do with the created mocks?
  2. Pass the mocks through the various steps in the scenario.

My only choice seems to be using a global variable for the mocks, which makes me somewhat uneasy.

Custom Test Method

Hello everyone,

first of all thank you for this awesome package.

I'm trying to add a custom test method in replacement of testWidgets. This new method has a function callback with 2 parameters instead of one. What is the best way to approach this, do you have any idea?

Here is my example code:

 testWidgetsWithPatrol('''Scaffold screenshot''', ($, tester) async {
      await $.pumpWidgetAndSettle(
        MaterialApp(
          home: Scaffold(
            appBar: AppBar(title: const Text('appx')),
            backgroundColor: Colors.blue,
          ),
        ),
      );

      expect($('app'), findsOneWidget);
      await $.native.pressHome();
    }, tags: ['patrol']);

Add reporter compatible with CucumberStudio

First of all, thank you! In my opinion this is the best package for writing widget tests as well as integration tests using BDD in terms of organization, ease of use and speed. Great work!

I think a great improvement would be added support for cucumber compatible Reporters so we can upload the test results to CucumberStudio using scripts. Other similar packages have that, but they are not as organized or easy to work with in my opinion.

Wrong method name

The built-in iDismissThePage step has the wrong method name in it.

Error in step code packages

First the error output when I run goldens update:

PS D:\fredgrttsstuff\GithubProjects\flutter_test\expert_test_setup> flutter test --update-goldens
00:03 +0: D:\fredgrttsstuff\GithubProjects\flutter_test\expert_test_setup\test\counter_test.dart: Counter Initial counter value is 0
══║ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK β•žβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
The following TestFailure object was thrown running a test:
Expected: exactly one matching node in the widget tree
Actual: _TextFinder:<zero widgets with text "0" (ignoring offstage widgets)>
Which: means none were found but one was expected

When the exception was thrown, this was the stack:
#4 iSeeText (package:bdd_widget_test/step/i_see_text.dart:8:3)
#5 main.. (file:///D:/fredgrttsstuff/GithubProjects/flutter_test/expert_test_setup/test/counter_test.dart:24:13)


(elided one frame from package:stack_trace)

This was caught by the test expectation on the following line:
package:bdd_widget_test/step/i_see_text.dart line 8
The test description was:
Initial counter value is 0
════════════════════════════════════════════════════════════════════════════════════════════════════
00:03 +0 -1: D:\fredgrttsstuff\GithubProjects\flutter_test\expert_test_setup\test\counter_test.dart: Counter Initial counter value is 0 [E]
Test failed. See exception logs above.
The test description was: Initial counter value is 0

00:03 +0 -1: D:\fredgrttsstuff\GithubProjects\flutter_test\expert_test_setup\test\counter_test.dart: Counter Add button increments the counter
══║ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK β•žβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
The following assertion was thrown running a test:
The finder "zero widgets with icon "IconData(U+0E047)" (ignoring offstage widgets)" (used in a call
to "tap()") could not find any matching widgets.

When the exception was thrown, this was the stack:
#0 WidgetController._getElementPoint (package:flutter_test/src/controller.dart:902:7)
#1 WidgetController.getCenter (package:flutter_test/src/controller.dart:841:12)
#2 WidgetController.tap (package:flutter_test/src/controller.dart:273:18)
#3 iTapIcon (package:bdd_widget_test/step/i_tap_icon.dart:9:16)
#4 main.. (file:///D:/fredgrttsstuff/GithubProjects/flutter_test/expert_test_setup/test/counter_test.dart:29:13)


(elided one frame from package:stack_trace)

The test description was:
Add button increments the counter
════════════════════════════════════════════════════════════════════════════════════════════════════
00:03 +0 -2: D:\fredgrttsstuff\GithubProjects\flutter_test\expert_test_setup\test\counter_test.dart: Counter Add button increments the counter [E]
Test failed. See exception logs above.
The test description was: Add button increments the counter

00:10 +7 -2: Some tests failed.
PS D:\fredgrttsstuff\GithubProjects\flutter_test\expert_test_setup>

the rep where the expert-est-setup project is at is
https://github.com/fredgrott/flutter_test

I did look at both:
https://github.com/olexale/bdd_widget_test/blob/master/lib/step/i_tap_icon.dart

and

https://github.com/olexale/bdd_widget_test/blob/master/lib/step/i_see_text.dart

things that might stand out:

one has a missing wait in async block

and let's see on null saefy String and IconData fields should they have late modfiers?

The error report does not mentioned nUll os it might not be that.

Any thoughts on what it might be?

Passing constructor parameter to widget under test

Hey @olexale
Great plugin! I have a quick question regarding passing constructor parameters. Is it possible to pass parameters from the GIVEN step to the WHEN step so that I can prepare the widget under test? I am not using get_it or provider to inject dependencies but a manual DI solution.

How to group certain features by using tags?

For integration test running in a browser, is there any configuration to set tags and using them to group feature files?

It seems that unlike flutter test command, flutter drive command does not provide --tag option

Another question is when a feature file fails, how to capture the screenshot at fail time?

Thanks

The name 'bddSetUp' is already defined.

When defining several Features within the .feature file, each with Background, then the test will fail, because of bddSetUp being defined several times.

This feature file:

Feature: Successful login
  Background:
    Given The app is running

  Scenario:
    Given I'm in the login screen
    And I have valid login credentials
    When I enter my valid credentials
    Then I should see the home screen

Feature: Unsuccessful login
  Background:
    Given The app is running

  Scenario:
    Given I'm in the login screen
    And I don't have valid login credentials
    When I enter my invalid credentials
    Then I should see {'Invalid credentials'} text

Will generate this Dart file:

void main() {
  Future<void> bddSetUp(WidgetTester tester) async {
    await theAppIsRunning(tester);
  }
  group('''Successful login''', () {
    testWidgets('''''', (tester) async {
      await bddSetUp(tester);
      await imInTheLoginScreen(tester);
      await iHaveValidLoginCredentials(tester);
      await iEnterMyValidCredentials(tester);
      await iShouldSeeTheHomeScreen(tester);
    });
  });
  Future<void> bddSetUp(WidgetTester tester) async {
    await theAppIsRunning(tester);
  }
  group('''Unsuccessful login''', () {
    testWidgets('''''', (tester) async {
      await bddSetUp(tester);
      await imInTheLoginScreen(tester);
      await iDontHaveValidLoginCredentials(tester);
      await iEnterMyInvalidCredentials(tester);
      await iShouldSeeText(tester, 'Invalid credentials');
    });
  });
}

Maybe the function bddSetUp() should not be defined outside of the group but inside the group, like this:

void main() {
  group('''Successful login''', () {
    Future<void> bddSetUp(WidgetTester tester) async {
        await theAppIsRunning(tester);
    }

    testWidgets('''''', (tester) async {
      await bddSetUp(tester);
      await imInTheLoginScreen(tester);
      await iHaveValidLoginCredentials(tester);
      await iEnterMyValidCredentials(tester);
      await iShouldSeeTheHomeScreen(tester);
    });
  });
  group('''Unsuccessful login''', () {
    Future<void> bddSetUp(WidgetTester tester) async {
        await theAppIsRunning(tester);
    }

    testWidgets('''''', (tester) async {
      await bddSetUp(tester);
      await imInTheLoginScreen(tester);
      await iDontHaveValidLoginCredentials(tester);
      await iEnterMyInvalidCredentials(tester);
      await iShouldSeeText(tester, 'Invalid credentials');
    });
  });
}

Support example table for scenario outline

Hi, it would be great if the plugin could support a scenario outline table. Currently, we want to use the plugin in combination with X-Ray. Our QA writes a value table like in the following example:

Scenario: Users is able to create a New Home (only required fields)
    Given The user is registered
    And The user is logged
    And The USER_MENU page is opened
    When The user tapped the +HOME button
    And The SAVE button is inactive
    And The user filled in the required text fields: <Type of home>, <Address>, <Adults>
        | Type of home | Address                    | Adults |
        | House        | Nedre Storgate, 42 Drammen | 2      |
    Then The SAVE button becomes active
    And The user tapped the SAVE button
    And The user is redirected to the MY_HOME page
    And The Home is created
    And The text fields are contain entered data: <Type of home>, <Address>, <Adults>
        | Type of home | Address                    | Adults |
        | House        | Nedre Storgate, 42 Drammen | 2      |

Without this functionality, there would be some limitations in the BDD process. Currently, we are trying to make some changes to the plugin to accommodate such tables and would be happy to help with any future improvements to the plugin. If there are other suggestions for similar improvements it would also be great. Thank you.

Use patrol and golden_toolkit in the same feature

Hi, first I want to thank you for this awesome package, it's really great and well documented!

Is there any workaround to use both patrol and golden_toolkit in the same scenario?

I've tried different ways but couldn't find a way to work it. I saw the #44 and I'm playing around the testMethods/testName/testerType to create a new type that have the PatrolTester and WidgetTester and wrap the goldenTests/patrol testers with my own method that pass the correct variable accordingly to what's need and use this type in the steps.

I would like to know if you came with this problem and maybe solved it or if you find a useful way to do it.


Edit

After playing a bit I found a useful way to do it. Will put here if someone needs to do the same:

Added this option to buid.yaml

testMethodName: testGoldensWithPatrol
testerName: tester
testerType: CustomTester

Create a file with this new types

import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:golden_toolkit/golden_toolkit.dart';
import 'package:patrol_finders/patrol_finders.dart';
import 'package:meta/meta.dart';

typedef WidgetTesterWithPatrolCallback = Future<void> Function(CustomTester);

typedef CustomTester = ({PatrolTester $, WidgetTester tester});


@isTest
Future<void> testGoldensWithPatrol(
    String description, WidgetTesterWithPatrolCallback callback) async {
  return testGoldens(description, (widgetTester) async {
    const config = PatrolTesterConfig();

    final patrolTester = PatrolTester(
      tester: widgetTester,
      config: config,
    );

    await callback(($: patrolTester, tester: widgetTester));
  });
}

Then I added the import this file on my .feature files and in the steps I use the CustomTester, generally using patrol but using the widgetTester as need to compare the goldens.

When you add a datatable as anything but the last step within background, all steps after the datatable get deleted

So with the change I made in #60 I have found an issue I'm having difficulty fixing.

An example:

Background:
    Given the following songs
    | artist           | title                |
    | 'The Beatles'    | 'Let It Be'          |
    | 'Camel'          | 'Slow yourself down' |
    And I wait

  Scenario: Testing scenario
    Given the following users exist <name> <twitter>
    | name            | twitter       |
    | 'Oleksandr'     | '@olexale'    |
    | 'Flutter'       | '@FlutterDev' |

should give:

// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: unused_import, directives_ordering

import 'package:bdd_widget_test/data_table.dart' as bdd;
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

import './step/the_following_songs.dart';
import './step/i_wait.dart';
import './step/the_following_users_exist.dart';

void main() {
  group(\'\'\'Testing feature\'\'\', () {
    Future<void> bddSetUp(WidgetTester tester) async {
      await theFollowingSongs(tester, const bdd.DataTable([[artist, title], ['The Beatles', 'Let It Be'], ['Camel', 'Slow yourself down']]));
      await iWait(tester);
    }
    testWidgets(\'\'\'Testing scenario\'\'\', (tester) async {
      await bddSetUp(tester);
      await theFollowingUsersExist(tester, 'Oleksandr', '@olexale');
      await theFollowingUsersExist(tester, 'Flutter', '@FlutterDev');
    });
  });
}

but currently gives:

// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: unused_import, directives_ordering

import 'package:bdd_widget_test/data_table.dart' as bdd;
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

import './step/the_following_songs.dart';
import './step/the_following_users_exist.dart';

void main() {
  group(\'\'\'Testing feature\'\'\', () {
    Future<void> bddSetUp(WidgetTester tester) async {
      await theFollowingSongs(tester, const bdd.DataTable([[artist, title], ['The Beatles', 'Let It Be'], ['Camel', 'Slow yourself down']]));
    }
    testWidgets(\'\'\'Testing scenario\'\'\', (tester) async {
      await bddSetUp(tester);
      await theFollowingUsersExist(tester, 'Oleksandr', '@olexale');
      await theFollowingUsersExist(tester, 'Flutter', '@FlutterDev');
    });
  });
}

With this issue I would also like to ask how you can debug a project like this? If It try to run tests from file instead of the overarching flutter test command, I get the following error: Dart library 'dart:ui' is not available on this platform

Add support for data tables in background and after

Maybe a bit backwards to make the pr before the issue, but I am in need to be able to use data tables in the background.

Background:
Given I have an account with
| "login" | "[email protected]" |
| "pin" | "12345" |
And the app is running
And I am on the {'/login'} screen

This is an example of how I wish to use it.

Nested brackets

The parser fails to parse the following line:

Given the parser should parse {'{"js": "code"}'} command

runAsync option?

Is there a way to use tester.runAsync(() async { .... }); option with this package using code-generation?

Documentation/FAQ: background and compare/contrast

The https://pub.dev/packages/flutter_gherkin author got a bit of a head start on this and so I'm comparing and contrasting for my company.

Can you please share some thoughts on pros/cons from your perspective - eg were you aware of his project, and whether yes/no, what are you trying to do that he isn't doing?

I'm also checking into it myself. Too bad i can't use both if desired. it seems nice that yours is a dev dependency only, and your youtube video is great, and if he doesnt' support widget tests in the "test" folder that's pretty big...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.