Skip to main content

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 optional bool; with false it 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:

OptionConfiguresDetails
Because(reason)the reason in the failure messageAnatomy
IgnoringCase(), IgnoringLeadingWhiteSpace(), …how strings are comparedString
Using(comparer)a custom equality comparerObject
Equivalent()a comparison by equivalency instead of equalityEquivalency
Within(tolerance)a tolerance for numbers and timesNumber
InAnyOrder(), IgnoringDuplicates(), …how collections are comparedCollections
Once(), Never(), AtLeast(2), AtLeast().Twice(), …how often an item must occurContained items
Within(timeout), WithTimeout(…), WithCancellation(…)how long an expectation may take or waitTime 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 IsInAscendingOrder or AreUnique
  • 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:

OptionEffect
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.