Context<T> class abstract final

The context for a Subject that allows asserting expectations and creating nested subjects.

A Subject is the target for checking expectations in a test. Every subject has a Context which holds the "actual" value, tracks how the value was obtained, and can check expectations about the value.

The user focused APIs called within tests are expectation extension methods written in an extension on Subject, typically specialized to a specific generic.

Expectation extension methods will make a call to one of the APIs on the subject's Context, and can perform one of two types of operations:

Whichever type of operation, an expectation extension method provides two callbacks. The first callback is an Iterable<String> Function() returning a description of the expectation. The second callback always takes the actual value as an argument, and the specific signature varies by operation.

In expectation extension methods calling expect, expectAync, or expectUnawaited, the predicate callback can report a Rejection if the value fails to satisfy the expectation. The description will be passed in a "clause" callback. The clause callback returns a description of what is checked which stands on its own. For instance the is equal to <1> in:

Expected: a int that:
  is equal to <1>

In expectation extension methods calling nest or nestAsync, the extract callback can return a Extracted.rejection if the value fails to satisfy an expectation which disallows extracting the value, or an Extracted.value to become the value in a nested subject. The description will be passed in a "label" callback. The label callback returns a description of the extracted subject as it relates to the original subject. For instance the completes to a value in:

Expected a Future<int> that:
  completes to a value that:
    is equal to <1>

A label should also be sensible when it is read as a clause. If no further expectations are checked on the extracted subject, or if the extraction is rejected, the "that:" is omitted in the output.

  Expected a Future<int> that:
    completes to a value

A rejection carries two descriptions, one description of the "actual" value that was tested, and an optional "which" with further details about how the result different from the expectation. If the "actual" argument is omitted it will be filled with a representation of the value passed to the expectation callback formatted with literal. If an expectation extension method is written on a type of subject without a useful toString(), the rejection can provide a string representation to use instead. The "which" argument may be omitted if the reason is very obvious based on the clause and "actual" description, but most expectations should include a "which".

The behavior of a context following a rejection depends on the source of the Subject.

When an expectation is rejected for a check subject, an exception is thrown to interrupt the test, so no further checks should happen. The failure message will include:

  • An "Expected" section with descriptions of all the expectations that were checked, including the ones that passed, and the last one that failed.
  • An "Actual" section, which may be the description directly from the Rejection if the failure was on the root subject, or may start with a partial version of the "Expected" description up to the label for the nesting subject that saw a failure, then the "actual" from the rejection.
  • A "Which" description from the rejection, if it was included.

For example, if a failure happens on the root subject, the "actual" is taken directly from the rejection.

Expected: a Future<int> that:
  completes to a value
Actual: a future that completes as an error
Which: threw <UnimplementedError> at:
<stack trace>

But if the failure happens on a nested subject, the actual starts with a description of the nesting or non-nesting expectations that succeeded, up to nesting point of the failure, then the "actual" and "which" from the rejection are indented to that level of nesting.

Expected: a Future<int> that:
  completes to a value that:
    equals <1>
Actual: a Future<int> that:
  completes to a value that:
  Actual: <0>
  Which: are not equal
extension CustomChecks on Subject<CustomType> {
  void someExpectation() {
    context.expect(() => ['meets this expectation'], (actual) {
      if (_expectationIsMet(actual)) return null;
      return Rejection(which: ['does not meet this expectation']);
    });
  }

  Subject<Foo> get someDerivedValue =>
      context.nest('has someDerivedValue', (actual) {
        if (_cannotReadDerivedValue(actual)) {
          return Extracted.rejection(which: ['cannot read someDerivedValue']);
        }
        return Extracted.value(_readDerivedValue(actual));
      });

  // for field reads that will not get rejected, use `has`
  Subject<Bar> get someField => has((a) => a.someField, 'someField');
}

When an expectation is rejected for a subject within a call to softCheck or softCheckAsync a CheckFailure will be returned with the rejection, as well as a FailureDetail which could be used to format the same failure message thrown by the check subject.

The description of an expectation may never be shown to the user, so the callback may never be invoked. If all the conditions on a subject succeed, or if the failure detail for a failed softCheck is never read, the descriptions will be unused. String formatting for the descriptions should be performed in the callback, not ahead of time.

The context for a subject may hold a real "actual" value to test against, or it may have a placeholder within a call to describe. A context with a placeholder value will not invoke the callback to check expectations.

If both callbacks are invoked, the description callback will always be called strictly after the expectation callback is called.

Callbacks passed to a context should not throw.

Some contexts disallow certain interactions. Calls to expectAsync or nestAsync must not be performed by a condition callback passed to softCheck or describe. Use softCheckAsync or describeAsync for any condition which checks async expectations. Calls to expectUnawaited may not be performed by a condition callback passed to softCheck or softCheckAsync.

Expectation extension methods can access the context for the subject with the ContextExtension.

Description callbacks return an Iterable<String> where each element is a line in the output. Individual elements should not contain newlines. Utilities such as prefixFirst, postfixLast, and literal may be useful to format values which are potentially multiline.

Constructors

Context()

Properties

hashCode int
The hash code for this object.
no setterinherited
runtimeType Type
A representation of the runtime type of the object.
no setterinherited

Methods

expect(Iterable<String> clause(), Rejection? predicate(T)) → void
Expect that predicate will not return a Rejection for the checked value.
expectAsync(Iterable<String> clause(), FutureOr<Rejection?> predicate(T)) Future<void>
Expect that predicate will not result in a Rejection for the checked value.
expectUnawaited(Iterable<String> clause(), void predicate(T, void (Rejection))) → void
Expect that predicate will not invoke the passed callback with a Rejection at any point.
nest<R>(Iterable<String> label(), Extracted<R> extract(T), {bool atSameLevel = false}) Subject<R>
Extract a property from the value for further checking.
nestAsync<R>(Iterable<String> label(), FutureOr<Extracted<R>> extract(T), AsyncCondition<R>? nestedCondition) Future<void>
Extract an asynchronous property from the value for further checking.
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
toString() String
A string representation of this object.
inherited

Operators

operator ==(Object other) bool
The equality operator.
inherited