Nulls, types and ranges
editConditions and null values
static string? FindUser(int id) => id == 1 ? "Alice" : null;
Assert.Null(FindUser(2));
var user = Assert.NotNull(FindUser(1)); // returns the value as a non-nullable string
Assert.Equal("ALICE", user.ToUpperInvariant());
Assert.True(char.IsUpper(user[0]));
Assert.False(string.IsNullOrWhiteSpace(user));- True and False check a
boolor abool?. Prefer a specialized assertion when there is one: the failure message ofAssert.True(list.Contains(item))only says that the condition is false, whileAssert.Contains(item, list)shows the list. The analyzer suggests the specialized assertion. - Null and NotNull accept references and nullable value types. NotNull returns the value with its non-nullable type, so the rest of the test needs no
!. - A non-nullable value type is never
null: the analyzer reports Null and NotNull on one as an error (MFAS0008,MFAS0009).
Types
object value = new List<int> { 1, 2 };
var list = Assert.IsType<List<int>>(value); // exactly this type, and returned as it
Assert.HasCount(2, list);
Assert.IsNotType<int[]>(value);
var items = Assert.IsAssignableTo<IEnumerable<int>>(value); // value is IEnumerable<int>
Assert.Contains(2, items);
Assert.IsNotAssignableTo<string>(value);| Assertion | Passes when | Returns |
|---|---|---|
IsType<T>(value) | value is exactly of type T | value as a T |
IsNotType<T>(value) | value is not exactly of type T | |
IsAssignableTo<T>(value) | value is T: the type, a derived type or an implemented interface | value as a T |
IsNotAssignableTo<T>(value) | value is not T |
- A
nullvalue fails IsType and IsAssignableTo; after them, the compiler knows the value is notnull. - A boxed
intis considered to be exactly of typeint?, as boxing anint?produces a boxedint. - IsAssignableTo matches what
value is Tmatches, including COM objects andIDynamicInterfaceCastableimplementations. - Each assertion has an overload that takes a
Typeinstead of a type parameter:Assert.IsType(typeof(List<int>), value). - No value is exactly of an abstract or static type: the analyzer reports IsType with one as an error (
MFAS0003).
Ranges
var percentage = 42;
Assert.InRange(percentage, 0, 100); // the value comes first, then the inclusive bounds
Assert.NotInRange(percentage, 50, 100);
Assert.InRange("banana", "apple", "cherry", StringComparer.Ordinal);
Assert.NotInRange(double.NaN, double.MinValue, double.MaxValue);InRange(actual, low, high)passes whenlow <= actual <= high: both bounds are included. The value under test comes first, as there is no single expected value.- The values are compared with
Comparer<T>.Default, or with theIComparer<T>you pass. - Without a comparer,
NaNand anullNullable<T>, as the value or as a bound, are never in range, likelow <= value && value <= high. - NotInRange passes when the value is outside the range.
InRange and NotInRange also accept a ref struct type that implements IComparable<T>:
var temperature = new Temperature(20);
Assert.InRange(temperature, new Temperature(0), new Temperature(25)); // compared with IComparable<Temperature>.CompareTo
Assert.NotInRange(temperature, new Temperature(21), new Temperature(25));The failure message shows the expressions of the bounds and the type of the values, as a ref struct value cannot be displayed. See Compare ref structs for the Temperature type.