Options
Many expectations share the same options. An option is appended to the expectation it configures. These rules apply to all options:
- Options can be chained in any order.
- A repeated option replaces the earlier one, e.g.
IgnoringCase().IgnoringCase(false)compares case-sensitively. - An option that switches something on, such as
IgnoringCase(), takes an optionalbool; withfalseit behaves as if it was not specified. - Options that set the same thing in different ways, e.g.
AsPrefix().AsSuffix(), throw.
The reference pages describe the details:
| Option | Configures | Details |
|---|---|---|
Because(reason) | the reason in the failure message | Anatomy |
IgnoringCase(), IgnoringLeadingWhiteSpace(), … | how strings are compared | String |
Using(comparer) | a custom equality comparer | Object |
Equivalent() | a comparison by equivalency instead of equality | Equivalency |
Within(tolerance) | a tolerance for numbers and times | Number |
InAnyOrder(), IgnoringDuplicates(), … | how collections are compared | Collections |
Once(), Never(), AtLeast(2), AtLeast().Twice(), … | how often an item must occur | Contained items |
Within(timeout), WithTimeout(…), WithCancellation(…) | how long an expectation may take or wait | Time and cancellation |
Because
Because(reason) adds a reason to the failure message, after the expectation it belongs to:
string title = "Let It Be";
await That(title).IsEqualTo("Let It Be").Because("it is the title of the last album");
How Because applies to Expect.ThatAll and Expect.ThatAny is described in
combining expectations.
String options
The expectations that compare strings, e.g. IsEqualTo, StartsWith or Contains, but also the items of a
collection of strings or the message of an exception, can ignore the casing, the newline style, the indentation or
leading and trailing whitespace:
string title = " Abbey Road ";
await That(title).IsEqualTo("abbey road")
.IgnoringCase()
.IgnoringLeadingWhiteSpace()
.IgnoringTrailingWhiteSpace();
await That(["Let It Be", "Help!"]).Contains("LET IT BE").IgnoringCase();
IsEqualTo can also match a wildcard or regex pattern with AsWildcard() or AsRegex(), see
match types.
Comparer
Using(comparer) replaces the default equality with a custom IEqualityComparer<T>:
string title = "Abbey Road";
await That(title).IsEqualTo("ABBEY ROAD").Using(StringComparer.OrdinalIgnoreCase);
await That(new Album("Abbey Road")).IsEqualTo(new Album("Abbey Road")).Using(new AlbumComparer());
Equivalency
Equivalent() switches an equality expectation to equivalency, which compares the
public members recursively instead of calling Equals:
Album[] albums = [new("Abbey Road"), new("Let It Be")];
await That(new Album("Abbey Road")).IsEqualTo(new Album("Abbey Road")).Equivalent();
await That(albums).Contains(new Album("Let It Be")).Equivalent();
Tolerance
Within(tolerance) accepts a value that differs from the expected one by at most the tolerance. It is available for
numbers, TimeSpan, DateTime, DateTimeOffset, DateOnly and TimeOnly (the latter two
on .NET 8 or later), and for the items of a collection of
them:
double duration = 4.02;
DateTime releaseDate = new DateTime(1970, 5, 8, 12, 0, 0);
await That(duration).IsEqualTo(4.0).Within(0.05);
await That(releaseDate).IsEqualTo(new DateTime(1970, 5, 8)).Within(TimeSpan.FromDays(1));
await That([3.98, 4.01]).All().AreEqualTo(4.0).Within(0.05);
Default tolerance
On Windows the DateTime resolution is about 10 to 15 milliseconds, so
comparing them as exact values might result in brittle tests. Therefore, it is possible to specify a default tolerance
that is used when a DateTime, DateTimeOffset, DateOnly, TimeOnly or TimeSpan subject is compared directly
(e.g. with IsEqualTo, IsOneOf, IsBefore or IsBetween) and no explicit tolerance is given:
using aweXpect.Chronology; // from the aweXpect.Chronology package
using aweXpect.Customization;
using IDisposable scope = Customize.aweXpect.Settings().DefaultTimeComparisonTolerance.Set(15.Milliseconds());
It also applies to the items of a collection of these types. An explicit Within always replaces the default
tolerance. The applied default tolerance is part of the failure message, for example
is equal to 2024-12-24T13:15:00.0000000 ± 0:00.015, unless it is zero. For a DateOnly, see
tolerance.
Where the default tolerance applies
It applies to the items of a collection of DateTime, DateTimeOffset, DateOnly, TimeOnly or TimeSpan values
(or their nullable counterparts) in every expectation that compares them with expected items, e.g. IsEqualTo,
Contains, IsContainedIn, StartsWith, EndsWith, HasItem or All().AreEqualTo, and in their negations. The same
holds for the values of a dictionary in Contains(key, value), ContainsValue, ContainsValues and IsEqualTo, and in
their negations.
The default tolerance is not used for:
- property verifications like
HasOffset() - collection expectations that don't compare items with expected items, like
IsInAscendingOrderorAreUnique - the keys of a dictionary, which are looked up through its key comparer, e.g. in
ContainsKey - values and members compared by
IsEquivalentTo - values compared as
object
Collection options
The expectations that compare a collection with another collection take the following options:
| Option | Effect |
|---|---|
InAnyOrder() | ignores the order of the items |
IgnoringDuplicates() | ignores repeated items |
IgnoringInterspersedItems() | allows other items between the expected ones (Contains, IsContainedIn) |
Properly() | requires a proper subset or superset (Contains, IsContainedIn) |
string[] songs = ["Two of Us", "Dig a Pony", "Let It Be"];
await That(songs).IsEqualTo(["Let It Be", "Two of Us", "Dig a Pony"]).InAnyOrder();
await That(songs).Contains(["Two of Us", "Let It Be"]).IgnoringInterspersedItems();
await That(songs).IsContainedIn(["Two of Us", "Dig a Pony", "Let It Be", "Get Back"]).Properly();
Time
Within(timeout) limits how long an expectation waits, e.g. for a condition, an event or a callback, and
WithTimeout(…) and WithCancellation(…) limit how long any expectation may take:
using aweXpect.Chronology; // from the aweXpect.Chronology package
Track track = new();
// Start a background task that plays the track
await That(track).Satisfies(x => x.IsPlayed).Within(2.Seconds())
.WithTimeout(5.Seconds());
See time and cancellation for the details.