Finder Matcher Gen
โ This package contains annotations for finder_matcher_gen. You cannot use this package independently of finder_matcher_gen.
- Motivation โจ
- Installation ๐ป
- Annotation usage ๐ฎ
- Generate code
- Detailed Documentation ๐
- Bugs/Request ๐
- Continuous Integration ๐ค
- Running Tests ๐งช
A Flutter package for generating custom Finder and Matcher for widget tests. Not sure what a Matcher or Finder is? Visit finder-matcher-gen documentation.
โจ Motivation
I find myself writing custom Finder and Matcher to abstract validation code for widget/integration tests. Implementing these can be tedious.
Finder-Matcher-Gen tries to fix this by implementing these custom classes for you.
Before | After | |
---|---|---|
Finder | ![]() |
![]() |
Matcher | ![]() |
![]() |
๐ป Installation
โ In order to start using Finder Matcher Gen you must have the Dart SDK installed on your machine.
Add finder_matcher_gen
to your pubspec.yaml
:
Run the following command on your terminal:
flutter pub add finder_matcher_annotation
flutter pub add finder_matcher_gen --dev
flutter pub add build_runner --dev
Or add it manually to your pubspec.yaml file.
dependencies:
finder_matcher_annotation: ^[version]
dev_dependencies:
finder_matcher_gen: ^[version]
build_runner: ^[version]
Run the command below to install.
Install it:
flutter pub get
Copy and paste the code below into your test file to import.
import 'package:finder_matcher_annotation/finder_matcher_annotation.dart';
๐ Annotation usage
Finder-matcher-gen makes use of annotation declarations to generate code. This tool provides two annotations: @Match
and @MatchDeclaration
annotations.
@Match annotation
Apply @Match
annotation to a declaration, usually, the test main()
function to specify widgets to generate a finder or matcher counterpart.
@Match(finders: [], matchers: [])
void main() {
//Test code in here
}
The @Match
annotation accepts two parameters: a list of Type
for finders; a list of MatchWidget
for matchers.
Finders
For a widget named TrafficLightLampWidget
pass the type of the widget to the finders param of the @Match
annotation to generate a finder counterpart.
@Match(finders: [TrafficLightLampWidget])
You can pass any number of widget types to the
finders
param to generate a finder counterpart.
Matcher
Pass a list of MatchWidget
to the matchers
param. MatchWidgets
accepts three params.
-
A required
type
: The runtime representation of this widget to generate a matcher counterpart. -
A required
matchSpecification
: An enum ofMatchSpecification
to define the kind of Matcher to generate for this widget. -
An optional
secondaryType
: The runtime representation of another widget that this Matcher will utilise.Some Matcher specifications involve a different widget. For example, to generate a matcher that asserts if
WidgetA
is contained inWidgetB
,WidgetB
will be passed as thesecondaryType
.
@Match(matchers: [
MatchWidget(TrafficLightLampWidget, MatchSpecification.matchesOneWidget),
])
To learn more about the different match specifications you can set, click here.
@MatchDeclaration annotation
In most cases, declarations (getters, fields, functions) defined in a widget are essential to the widgetโs identity. In other words, they will be used for asserting this widget behaviour.
Annotate widget fields, getters, or functions with @MatchDeclaration
to mark them for use in the validation code. The @MatchDeclaration
annotation accepts a defaultValue
argument used to compare to the actual value of the widget found in the test environment. A constructor field for this declaration will be added to the generated code if no default value is provided.
class RedTrafficLightLampWidget extends StatelessWidget{
@MatchDeclaration()
final Color lightColor;
@MatchDeclaration(defaultValue: 'STOP')
final String text;
}
The code below highlights the result of providing a default value and otherwise.
/// Where `_lightColor` is a constructor field of this generated code
return widget.lightColor == _lightColor && widget.text == 'STOP';
Linting
A common pitfall while using this annotation is passing a wrong data type (different from the data type of the annotated property) to the defaultValue
.
Fortunately, this package provides static analysis to throw an error when this kind of mistake is made.
Note: The annotation
@MatchDeclaration
can only be used on getters, fields, and non-void methods
๐ญ Generate code
Run the command below to generate the custom finder and matcher code.
flutter pub run build_runner build
After a successful run, you should notice two newly generated files.
- A
${my_test_file}.finders.dart
file containing generated finders. - A
${my_test_file}.matchers.dart
file containing generated matchers.
For more information, see generate section to explore how to use generated files.
Configure generation for integration tests
Create a build.yaml
file in the top-level folder of your project. Insert the code below in the newly created file.
targets:
$default:
sources:
- integration_test/**
- lib/**
- test/**
# Note that it is important to include these in the default target.
- pubspec.*
- $package$
You can also include custom folders where you need to generate matchers and finders.
๐ Detailed Documentation
For more detailed information of using this tool, visit the documentation.
๐ Bugs/Request
If you encounter any problems feel free to open an issue. If you feel the library is missing a feature, please raise a ticket on Github and I'll look into it. Pull requests are also welcomed.
๐ค Continuous Integration
Finder Matcher Gen comes with a built-in GitHub Actions workflow powered by Very Good Workflows but you can also add your preferred CI/CD solution.
Out of the box, on each pull request and push, the CI formats
, lints
, and tests
the code. This ensures the code remains consistent and behaves correctly as you add functionality or make changes. The project uses Very Good Analysis for a strict set of analysis options used by our team. Code coverage is enforced using the Very Good Workflows.