|
1 |
| -# Trying Checks as a Preview |
| 1 | +# Checking expectations with `checks` |
2 | 2 |
|
3 |
| -1. Add a git dependency on this package: |
| 3 | +Expectations start with `checkThat`. This utility returns a `Subject`, and |
| 4 | +expectations can be checked against the subject. Expectations are defined as |
| 5 | +extension methods, and different expectations will be available for subjects |
| 6 | +with different value types. |
4 | 7 |
|
5 |
| - ```yaml |
6 |
| - dev_dependencies: |
7 |
| - checks: |
8 |
| - git: |
9 |
| - url: https://github.com/dart-lang/test |
10 |
| - path: pkgs/checks |
11 |
| - # Omit to try the latest, or pin to a commit to avoid |
12 |
| - # breaking changes while the library is experimental. |
13 |
| - ref: <sha> |
14 |
| - ``` |
| 8 | +```dart |
| 9 | +checkThat(someValue).equals(expectedValue); |
| 10 | +checkThat(someList).deepEquals(expectedList); |
| 11 | +checkThat(someString).contains('expected pattern'); |
| 12 | +``` |
15 | 13 |
|
16 |
| -1. Add an import to `package:checks/checks.dart`. |
| 14 | +Multiple expectations can be checked against the same value using cascade |
| 15 | +syntax. When multiple expectations are checked against a single value, a failure |
| 16 | +will included descriptions of the expectations that already passed. |
| 17 | + |
| 18 | +```dart |
| 19 | +checkThat(someString) |
| 20 | + ..startsWith('a') |
| 21 | + ..endsWith('z') |
| 22 | + ..contains('lmno'); |
| 23 | +``` |
| 24 | + |
| 25 | +Some expectations return a `Subject` for another value derived from the original |
| 26 | +value - for instance reading a field or awaiting the result of a Future. |
| 27 | + |
| 28 | +```dart |
| 29 | +checkThat(someString).length.equals(expectedLength); |
| 30 | +(await checkThat(someFuture).completes()).equals(expectedCompletion); |
| 31 | +``` |
| 32 | + |
| 33 | +Fields can be extracted from objects for checking further properties with the |
| 34 | +`has` utility. |
| 35 | + |
| 36 | +```dart |
| 37 | +checkThat(someValue) |
| 38 | + .has((value) => value.property, 'property') |
| 39 | + .equals(expectedPropertyValue); |
| 40 | +``` |
| 41 | + |
| 42 | +Some expectations take arguments which are themselves expectations to apply to |
| 43 | +other values. These expectations take `Condition` arguments, which check |
| 44 | +expectations when they are applied to a `Subject`. The `ConditionSubject` |
| 45 | +utility acts as both a condition and a subject. Any expectations checked on the |
| 46 | +value as a subject will be recorded and replayed when it is applied as a |
| 47 | +condition. The `it()` utility returns a `ConditionSubject`. |
| 48 | + |
| 49 | +```dart |
| 50 | +checkThat(someList).any(it()..isGreaterThan(0)); |
| 51 | +``` |
| 52 | + |
| 53 | +Some complicated checks may be difficult to write with parenthesized awaited |
| 54 | +expressions, or impossible to write with cascade syntax. There are `which` |
| 55 | +utilities for both use cases which take a `Condition`. |
| 56 | + |
| 57 | +```dart |
| 58 | +checkThat(someString) |
| 59 | + ..startsWith('a') |
| 60 | + // A cascade would not be possible on `length` |
| 61 | + ..length.which(it() |
| 62 | + ..isGreatherThan(10) |
| 63 | + ..isLessThan(100)); |
| 64 | +
|
| 65 | +await checkThat(someFuture) |
| 66 | + .completes() |
| 67 | + .which(it()..equals(expectedCompletion)); |
| 68 | +``` |
| 69 | + |
| 70 | +# Writing custom expectations |
| 71 | + |
| 72 | +Expectations are written as extension on `Subject` with specific generics. The |
| 73 | +library `package:checks/context.dart` gives access to a `context` getter on |
| 74 | +`Subject` which offers capabilities for defining expectations on the subject's |
| 75 | +value. |
| 76 | + |
| 77 | +The `Context` allows checking a expectation with `expect`, `expectAsync` and |
| 78 | +`expectUnawaited`, or extracting a derived value for performing other checks |
| 79 | +with `nest` and `nestAsync`. Failures are reported by returning a `Rejection`, |
| 80 | +or an `Extracted.rejection`, extensions should avoid throwing exceptions. |
| 81 | + |
| 82 | +Descriptions of the clause checked by an expectations are passed through a |
| 83 | +separate callback from the predicate which checks the value. Nesting calls are |
| 84 | +made with a label directly. When there are no failures the clause callbacks are |
| 85 | +not called. When a `Condition` is described, the clause callbacks are called, |
| 86 | +but the predicate callbacks are not called. Conditions can be checked against |
| 87 | +values without throwing an exception using `softCheck` or `softCheckAsync`. |
| 88 | + |
| 89 | +```dart |
| 90 | +extension CustomChecks on Subject<CustomType> { |
| 91 | + void someExpectation() { |
| 92 | + context.expect(() => ['meets this expectation'], (actual) { |
| 93 | + if (_expectationIsMet(actual)) return null; |
| 94 | + return Rejection(which: ['does not meet this expectation']); |
| 95 | + }); |
| 96 | + } |
| 97 | +
|
| 98 | + Subject<Foo> get someDerivedValue => |
| 99 | + context.nest('has someDerivedValue', (actual) { |
| 100 | + if (_cannotReadDerivedValue(actual)) { |
| 101 | + return Extracted.rejection(which: ['cannot read someDerivedValue']); |
| 102 | + } |
| 103 | + return Extracted.value(_readDerivedValue(actual)); |
| 104 | + }); |
| 105 | +
|
| 106 | + // for field reads that will not get rejected, use `has` |
| 107 | + Subject<Bar> get someField => has((a) => a.someField, 'someField'); |
| 108 | +} |
| 109 | +``` |
| 110 | + |
| 111 | +# Trying Checks as a Preview |
| 112 | + |
| 113 | +1. Add a `dev_dependency` on `checks: ^0.1.0`. |
17 | 114 |
|
18 | 115 | 1. Replace the existing `package:test/test.dart` import with
|
19 | 116 | `package:test/scaffolding.dart`.
|
20 | 117 |
|
| 118 | +1. Add an import to `package:checks/checks.dart`. |
| 119 | + |
21 | 120 | 1. For an incremental migration within the test, add an import to
|
22 | 121 | `package:test/expect.dart`. Remove it to surface errors in tests that still
|
23 | 122 | need to be migrated, or keep it in so the tests work without being fully
|
|
0 commit comments