Object
Describes the possible expectations for any object.
| Expectation | Negated | Summary |
|---|---|---|
IsEqualTo | IsNotEqualTo | equal to the expected object |
IsSameAs | IsNotSameAs | the same reference as the expected object |
IsEquatableTo | IsNotEquatableTo | equal according to its IEquatable<T> implementation |
IsEquivalentTo | IsNotEquivalentTo | structurally equal to the expected object |
IsOneOf | IsNotOneOf | equal to one of the expected objects |
Is<T> | IsNot<T> | of type T or a derived type |
IsExactly<T> | IsNotExactly<T> | exactly of type T |
IsNull | IsNotNull | null |
Satisfies | DoesNotSatisfy | satisfies a predicate |
CompliesWith | DoesNotComplyWith | meets a nested expectation |
Whose | a member meets a nested expectation | |
IsCollected | IsNotCollected | the target of a WeakReference is garbage collected |
Equality
You can verify that the object is equal to another one or not:
record Album(string Title);
Album album = new("Abbey Road");
await That(album).IsEqualTo(new Album("Abbey Road"));
await That(album).IsNotEqualTo(new Album("Revolver"));
This uses the object.Equals(object?, object?) method.
Numbers typed as object
A number typed as object also equals a number of another numeric type with the same value (e.g. 1 and 1L)
in equality (IsEqualTo, IsOneOf, Contains, ContainsValue), but not in
equivalency. For Half, Int128 and UInt128 this only applies
on .NET 8 or later.
Reference equality
You can verify that the object has the same reference as another one:
record Album(string Title);
Album album = new("Abbey Road");
await That(album).IsSameAs(album);
await That(album).IsNotSameAs(new Album("Abbey Road"));
This uses the object.ReferenceEquals(object?, object?) method.
Custom comparer
You can verify that the object is equal to another one while using a custom IEqualityComparer<object>:
class AlbumComparer : IEqualityComparer<object>
{
public bool Equals(object? x, object? y)
=> (x as Album)?.Title == (y as Album)?.Title;
public int GetHashCode(object obj)
=> obj.GetHashCode();
}
Album album = new("Abbey Road");
await That(album).IsEqualTo(new Album("Abbey Road")).Using(new AlbumComparer());
IEquatable<T>
You can verify that the object is equal to a value by using its IEquatable<T> implementation, also when the value
has a different type:
class TrackId(long value) : IEquatable<long>
{
public bool Equals(long other) => value == other;
}
TrackId trackId = new(42);
await That(trackId).IsEquatableTo(42L);
await That(trackId).IsNotEquatableTo(7L);
Equivalency
You can verify that the object is structurally equivalent to another one. See the
equivalency page for details and configuration options:
Album album = new("Abbey Road");
await That(album).IsEquivalentTo(new Album("Abbey Road"));
await That(album).IsNotEquivalentTo(new Album("Revolver"));
Use IsEqualTo when the type defines what equal means, e.g. a record or a type that overrides Equals. Use
IsEquivalentTo when it does not, e.g. for a class that only has reference equality, or when some members should be
ignored. .Equivalent() switches an expectation that compares with Equals, e.g. Contains or All().AreEqualTo on
a collection, to the same equivalency.
One of
You can verify that the object is one of many alternatives:
record Album(string Title);
Album album = new("Abbey Road");
await That(album).IsOneOf([new Album("Abbey Road"), new Album("Revolver")]);
await That(album).IsNotOneOf([new Album("Revolver"), new Album("Help!")]);
Type check
You can verify that the object is of a given type or a derived type, or that it is not:
object album = new Album("Abbey Road");
await That(album).Is<Album>();
await That(album).Is(typeof(Album));
await That(album).IsNot<Track>();
await That(album).IsNot(typeof(Track));
IsExactly and IsNotExactly do not accept a derived type:
object album = new Album("Abbey Road");
await That(album).IsExactly<Album>();
await That(album).IsExactly(typeof(Album));
await That(album).IsNotExactly<Track>();
await That(album).IsNotExactly(typeof(Track));
Null
You can verify that the object is null or not:
object? album = null;
await That(album).IsNull();
await That(new Album("Abbey Road")).IsNotNull();
Condition
You can verify that any object satisfies a given predicate:
Track track = new() { Title = "Let It Be", PlayCount = 3 };
await That(track).Satisfies(x => x.PlayCount > 0);
await That(track).DoesNotSatisfy(x => x.IsPlayed);
When the object changes in the background, Within(…) waits until it satisfies the predicate, see
waiting for a condition.
Nested expectation
You can verify that any object complies with an expectation:
List<Track> tracks = new();
await That(tracks).CompliesWith(x => x.IsEmpty());
await That(tracks).DoesNotComplyWith(x => x.HasCount().GreaterThan(0));
DoesNotComplyWith succeeds when the nested expectation fails. Like Satisfies, CompliesWith can
wait for the object with Within(…).
Which one to use:
Satisfiesfor a quick check with a predicate; a failure only shows the predicate and the object.CompliesWithto reuse other expectations, e.g.IsEmpty()or a string option; a failure explains what the nested expectation found.Whoseto verify a member of the object; a failure names the member and its value.
Garbage collection
You can verify that the target of a WeakReference or WeakReference<T> is garbage collected or not:
using System.Runtime.CompilerServices;
WeakReference released = CreateReleasedReference();
Album album = new("Let It Be");
WeakReference<Album> cached = new(album);
await That(released).IsCollected();
await That(cached).IsNotCollected()
.Because("the album is still in use");
GC.KeepAlive(album);
[MethodImpl(MethodImplOptions.NoInlining)]
static WeakReference CreateReleasedReference()
=> new(new Album("Abbey Road"));
A strong reference that the test itself still holds keeps the target alive, e.g. a local variable or the temporary
object in new WeakReference(new Album(…)), which a Debug build keeps until the end of the method, even when it is no
longer used. The example above therefore creates the released album in a separate method. For an object the test
creates itself, use
IsCollectedAfter instead, which creates the object where no
reference to it remains.
How the garbage collection is triggered
Both force full blocking garbage collections before they check the target, and repeat them a few times, as finalizers can release further objects.