Skip to content
This repository was archived by the owner on Oct 22, 2024. It is now read-only.

Commit fcbd361

Browse files
authored
Add more advice away from predicate (#233)
Add a best practice entry and expand the dartdoc for `predicate` to mention that the failure messages are not as detailed as `TypeMatcher`, and that the latter is preferred when the value can fail in more than one way. Add a generic argument to the example usage - most cases should prefer to include a generic. Cleanup an unnecessary private `typedef` with a single usage.
1 parent 3d03fa1 commit fcbd361

File tree

2 files changed

+26
-6
lines changed

2 files changed

+26
-6
lines changed

README.md

+11
Original file line numberDiff line numberDiff line change
@@ -253,3 +253,14 @@ why the test failed. For instance compare the failures between
253253
Which: has length of <2>
254254
255255
```
256+
257+
### Prefer TypeMatcher to predicate if the match can fail in multiple ways
258+
259+
The `predicate` utility is a convenient shortcut for testing an arbitrary
260+
(synchronous) property of a value, but it discards context and failures are
261+
opaque. Different failure modes cannot be distinguished in the output which is
262+
determined by a single "description" argument. Using `isA<SomeType>()` and the
263+
`TypeMatcher.having` API to extract and test derived properties in a structured
264+
way brings the context of that structure through to failure messages, so
265+
failures for different reasons will have distinguishable and actionable failure
266+
messages.

lib/src/core_matchers.dart

+15-6
Original file line numberDiff line numberDiff line change
@@ -290,20 +290,29 @@ class _In<T> extends FeatureMatcher<T> {
290290
description.add('is in ').addDescriptionOf(_source);
291291
}
292292

293-
/// Returns a matcher that uses an arbitrary function that returns
294-
/// true or false for the actual value.
293+
/// Returns a matcher that uses an arbitrary function that returns whether the
294+
/// value is considered a match.
295295
///
296296
/// For example:
297297
///
298-
/// expect(v, predicate((x) => ((x % 2) == 0), "is even"))
298+
/// expect(actual, predicate<num>((v) => (v % 2) == 0, 'is even'));
299+
///
300+
/// Use this method when a value is checked for one conceptual property
301+
/// described by [description].
302+
///
303+
/// If the value can be rejected for more than one reason prefer using [isA] and
304+
/// the [TypeMatcher.having] API to build up a matcher with output that can
305+
/// distinquish between them.
306+
///
307+
/// Using an explicit generict argument allows a passed function literal to have
308+
/// an inferred argument type of [T], and values of the wrong type will be
309+
/// rejected with an informative message.
299310
Matcher predicate<T>(bool Function(T) f,
300311
[String description = 'satisfies function']) =>
301312
_Predicate(f, description);
302313

303-
typedef _PredicateFunction<T> = bool Function(T value);
304-
305314
class _Predicate<T> extends FeatureMatcher<T> {
306-
final _PredicateFunction<T> _matcher;
315+
final bool Function(T) _matcher;
307316
final String _description;
308317

309318
_Predicate(this._matcher, this._description);

0 commit comments

Comments
 (0)