- Add VariableSpec class with magnitude field validation - Add Formula class supporting multiple input/output variables - Support d4rt_code as string or object with code field - Add comprehensive tests for parsing and serialization - Fix broken test import in pruebas_d4rt_test.dart Follows README.md format requirements exactly
265 lines
9 KiB
Markdown
265 lines
9 KiB
Markdown
[](https://pub.dev/packages/matcher)
|
|
[](https://pub.dev/packages/matcher/publisher)
|
|
|
|
Support for specifying test expectations, such as for unit tests.
|
|
|
|
The matcher library provides a third-generation assertion mechanism, drawing
|
|
inspiration from [Hamcrest](https://code.google.com/p/hamcrest/).
|
|
|
|
For more information on testing, see
|
|
[Unit Testing with Dart](https://github.com/dart-lang/test/blob/master/pkgs/test/README.md#writing-tests).
|
|
|
|
## Using matcher
|
|
|
|
Expectations start with a call to [`expect()`] or [`expectAsync()`].
|
|
|
|
[`expect()`]: https://pub.dev/documentation/matcher/latest/expect/expect.html
|
|
[`expectAsync()`]: https://pub.dev/documentation/matcher/latest/expect/expectAsync.html
|
|
|
|
Any matchers package can be used with `expect()` to do
|
|
complex validations:
|
|
|
|
[`matcher`]: https://pub.dev/documentation/matcher/latest/matcher/matcher-library.html
|
|
|
|
```dart
|
|
import 'package:test/test.dart';
|
|
|
|
void main() {
|
|
test('.split() splits the string on the delimiter', () {
|
|
expect('foo,bar,baz', allOf([
|
|
contains('foo'),
|
|
isNot(startsWith('bar')),
|
|
endsWith('baz')
|
|
]));
|
|
});
|
|
}
|
|
```
|
|
|
|
If a non-matcher value is passed, it will be wrapped with [`equals()`].
|
|
|
|
[`equals()`]: https://pub.dev/documentation/matcher/latest/expect/equals.html
|
|
|
|
## Exception matchers
|
|
|
|
You can also test exceptions with the [`throwsA()`] function or a matcher such
|
|
as [`throwsFormatException`]:
|
|
|
|
[`throwsA()`]: https://pub.dev/documentation/matcher/latest/expect/throwsA.html
|
|
[`throwsFormatException`]: https://pub.dev/documentation/matcher/latest/expect/throwsFormatException-constant.html
|
|
|
|
```dart
|
|
import 'package:test/test.dart';
|
|
|
|
void main() {
|
|
test('.parse() fails on invalid input', () {
|
|
expect(() => int.parse('X'), throwsFormatException);
|
|
});
|
|
}
|
|
```
|
|
|
|
### Future Matchers
|
|
|
|
There are a number of useful functions and matchers for more advanced
|
|
asynchrony. The [`completion()`] matcher can be used to test `Futures`; it
|
|
ensures that the test doesn't finish until the `Future` completes, and runs a
|
|
matcher against that `Future`'s value.
|
|
|
|
[`completion()`]: https://pub.dev/documentation/matcher/latest/expect/completion.html
|
|
|
|
```dart
|
|
import 'dart:async';
|
|
|
|
import 'package:test/test.dart';
|
|
|
|
void main() {
|
|
test('Future.value() returns the value', () {
|
|
expect(Future.value(10), completion(equals(10)));
|
|
});
|
|
}
|
|
```
|
|
|
|
The [`throwsA()`] matcher and the various [`throwsExceptionType`] matchers work
|
|
with both synchronous callbacks and asynchronous `Future`s. They ensure that a
|
|
particular type of exception is thrown:
|
|
|
|
[`throwsExceptionType`]: https://pub.dev/documentation/matcher/latest/expect/throwsException-constant.html
|
|
|
|
```dart
|
|
import 'dart:async';
|
|
|
|
import 'package:test/test.dart';
|
|
|
|
void main() {
|
|
test('Future.error() throws the error', () {
|
|
expect(Future.error('oh no'), throwsA(equals('oh no')));
|
|
expect(Future.error(StateError('bad state')), throwsStateError);
|
|
});
|
|
}
|
|
```
|
|
|
|
The [`expectAsync()`] function wraps another function and has two jobs. First,
|
|
it asserts that the wrapped function is called a certain number of times, and
|
|
will cause the test to fail if it's called too often; second, it keeps the test
|
|
from finishing until the function is called the requisite number of times.
|
|
|
|
```dart
|
|
import 'dart:async';
|
|
|
|
import 'package:test/test.dart';
|
|
|
|
void main() {
|
|
test('Stream.fromIterable() emits the values in the iterable', () {
|
|
var stream = Stream.fromIterable([1, 2, 3]);
|
|
|
|
stream.listen(expectAsync1((number) {
|
|
expect(number, inInclusiveRange(1, 3));
|
|
}, count: 3));
|
|
});
|
|
}
|
|
```
|
|
|
|
[`expectAsync()`]: https://pub.dev/documentation/matcher/latest/expect/expectAsync.html
|
|
|
|
### Stream Matchers
|
|
|
|
The `test` package provides a suite of powerful matchers for dealing with
|
|
[asynchronous streams][Stream]. They're expressive and composable, and make it
|
|
easy to write complex expectations about the values emitted by a stream. For
|
|
example:
|
|
|
|
[Stream]: https://api.dart.dev/stable/dart-async/Stream-class.html
|
|
|
|
```dart
|
|
import 'dart:async';
|
|
|
|
import 'package:test/test.dart';
|
|
|
|
void main() {
|
|
test('process emits status messages', () {
|
|
// Dummy data to mimic something that might be emitted by a process.
|
|
var stdoutLines = Stream.fromIterable([
|
|
'Ready.',
|
|
'Loading took 150ms.',
|
|
'Succeeded!'
|
|
]);
|
|
|
|
expect(stdoutLines, emitsInOrder([
|
|
// Values match individual events.
|
|
'Ready.',
|
|
|
|
// Matchers also run against individual events.
|
|
startsWith('Loading took'),
|
|
|
|
// Stream matchers can be nested. This asserts that one of two events are
|
|
// emitted after the "Loading took" line.
|
|
emitsAnyOf(['Succeeded!', 'Failed!']),
|
|
|
|
// By default, more events are allowed after the matcher finishes
|
|
// matching. This asserts instead that the stream emits a done event and
|
|
// nothing else.
|
|
emitsDone
|
|
]));
|
|
});
|
|
}
|
|
```
|
|
|
|
A stream matcher can also match the [`async`] package's [`StreamQueue`] class,
|
|
which allows events to be requested from a stream rather than pushed to the
|
|
consumer. The matcher will consume the matched events, but leave the rest of the
|
|
queue alone so that it can still be used by the test, unlike a normal `Stream`
|
|
which can only have one subscriber. For example:
|
|
|
|
[`async`]: https://pub.dev/packages/async
|
|
[`StreamQueue`]: https://pub.dev/documentation/async/latest/async/StreamQueue-class.html
|
|
|
|
```dart
|
|
import 'dart:async';
|
|
|
|
import 'package:async/async.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
void main() {
|
|
test('process emits a WebSocket URL', () async {
|
|
// Wrap the Stream in a StreamQueue so that we can request events.
|
|
var stdout = StreamQueue(Stream.fromIterable([
|
|
'WebSocket URL:',
|
|
'ws://localhost:1234/',
|
|
'Waiting for connection...'
|
|
]));
|
|
|
|
// Ignore lines from the process until it's about to emit the URL.
|
|
await expectLater(stdout, emitsThrough('WebSocket URL:'));
|
|
|
|
// Parse the next line as a URL.
|
|
var url = Uri.parse(await stdout.next);
|
|
expect(url.host, equals('localhost'));
|
|
|
|
// You can match against the same StreamQueue multiple times.
|
|
await expectLater(stdout, emits('Waiting for connection...'));
|
|
});
|
|
}
|
|
```
|
|
|
|
The following built-in stream matchers are available:
|
|
|
|
* [`emits()`] matches a single data event.
|
|
* [`emitsError()`] matches a single error event.
|
|
* [`emitsDone`] matches a single done event.
|
|
* [`mayEmit()`] consumes events if they match an inner matcher, without
|
|
requiring them to match.
|
|
* [`mayEmitMultiple()`] works like `mayEmit()`, but it matches events against
|
|
the matcher as many times as possible.
|
|
* [`emitsAnyOf()`] consumes events matching one (or more) of several possible
|
|
matchers.
|
|
* [`emitsInOrder()`] consumes events matching multiple matchers in a row.
|
|
* [`emitsInAnyOrder()`] works like `emitsInOrder()`, but it allows the
|
|
matchers to match in any order.
|
|
* [`neverEmits()`] matches a stream that finishes *without* matching an inner
|
|
matcher.
|
|
|
|
You can also define your own custom stream matchers with [`StreamMatcher()`].
|
|
|
|
[`emits()`]: https://pub.dev/documentation/matcher/latest/expect/emits.html
|
|
[`emitsError()`]: https://pub.dev/documentation/matcher/latest/expect/emitsError.html
|
|
[`emitsDone`]: https://pub.dev/documentation/matcher/latest/expect/emitsDone.html
|
|
[`mayEmit()`]: https://pub.dev/documentation/matcher/latest/expect/mayEmit.html
|
|
[`mayEmitMultiple()`]: https://pub.dev/documentation/matcher/latest/expect/mayEmitMultiple.html
|
|
[`emitsAnyOf()`]: https://pub.dev/documentation/matcher/latest/expect/emitsAnyOf.html
|
|
[`emitsInOrder()`]: https://pub.dev/documentation/matcher/latest/expect/emitsInOrder.html
|
|
[`emitsInAnyOrder()`]: https://pub.dev/documentation/matcher/latest/expect/emitsInAnyOrder.html
|
|
[`neverEmits()`]: https://pub.dev/documentation/matcher/latest/expect/neverEmits.html
|
|
[`StreamMatcher()`]: https://pub.dev/documentation/matcher/latest/expect/StreamMatcher-class.html
|
|
|
|
## Best Practices
|
|
|
|
### Prefer semantically meaningful matchers to comparing derived values
|
|
|
|
Matchers which have knowledge of the semantics that are tested are able to emit
|
|
more meaningful messages which don't require reading test source to understand
|
|
why the test failed. For instance compare the failures between
|
|
`expect(someList.length, 1)`, and `expect(someList, hasLength(1))`:
|
|
|
|
```
|
|
// expect(someList.length, 1);
|
|
Expected: <1>
|
|
Actual: <2>
|
|
```
|
|
|
|
```
|
|
// expect(someList, hasLength(1));
|
|
Expected: an object with length of <1>
|
|
Actual: ['expected value', 'unexpected value']
|
|
Which: has length of <2>
|
|
|
|
```
|
|
|
|
### Prefer TypeMatcher to predicate if the match can fail in multiple ways
|
|
|
|
The `predicate` utility is a convenient shortcut for testing an arbitrary
|
|
(synchronous) property of a value, but it discards context and failures are
|
|
opaque. Different failure modes cannot be distinguished in the output which is
|
|
determined by a single "description" argument. Using `isA<SomeType>()` and the
|
|
`TypeMatcher.having` API to extract and test derived properties in a structured
|
|
way brings the context of that structure through to failure messages, so
|
|
failures for different reasons will have distinguishable and actionable failure
|
|
messages.
|