Extending aweXpect
This library will never be able to cope with all ideas and use cases. Therefore, it is possible to use the
aweXpect.Core package and write your own extensions.
This package aims to be more stable than the main aweXpect package, to reduce the risk of version conflicts between
different extensions.
Reference aweXpect.Core in your extension, not aweXpect, and use the lowest version that contains the API you
need, so that the extension works together with as many versions of aweXpect as possible. The namespace
aweXpect.Core.Internal may change in any version, so don't use it in the code that your extension ships.
Keep your extensions trimmable and AOT compatible like aweXpect itself, as described in Native AOT for extensions.
| Page | Topics |
|---|---|
| Generated expectations | expectations generated from a method that checks a condition |
| Constraints and results | constraints, result helpers, null subjects, negation, argument checks |
| Options and match types | options of your own results, time tolerances, custom comparisons |
| Collections and nested expectations | collection subjects, expectations on members and on collection items |
| Asynchronous expectations | asynchronous constraints, cancellation, repeated checks |
| Message conventions | how the failure messages of an extension should read |
| Customization values | customization values of your own, with lifetimes |
| Initialization | value formatters and test framework adapters |
| Native AOT for extensions | the metadata your expectations need under Native AOT |
Your first expectation
Namespaces used in this section
using aweXpect.Core;
using aweXpect.Results;
The samples on this and the following pages verify tracks of the following type, such as "Love Me Do" (2:22) or "Hey Jude" (7:11):
public record Track(string Title, TimeSpan Duration);
Generated from a condition
If you want to verify that a Track is radio friendly, i.e. that it lasts at most three minutes, the simplest way is a
method that checks the condition, marked with [GenerateExpectation]:
using aweXpect.Core.Extending;
public static partial class ThatTrack
{
[GenerateExpectation("Is{Not}RadioFriendly", "is {not} radio friendly")]
private static bool IsRadioFriendly(Track track)
=> track.Duration <= TimeSpan.FromMinutes(3);
}
The source generator in aweXpect.Core writes IsRadioFriendly() and IsNotRadioFriendly() with their failure
messages, see generated expectations. With [GenerateCollectionExpectation], the
same method also verifies the items of a collection, e.g. That(tracks).All().AreRadioFriendly().
When an expectation needs more than a condition, e.g. options, a different result or a nested expectation, write it yourself as described in the rest of this section.
Expectations
You can extend the expectations for any types, by adding extension methods on IThat<TType>.
To write IsRadioFriendly yourself, you specify the following method signature:
/// <summary>
/// Verifies that the <paramref name="subject"/> is radio friendly, i.e. that it lasts at most three minutes.
/// </summary>
public static AndOrResult<Track, IThat<Track?>> IsRadioFriendly(this IThat<Track?> subject)
{
// ...
}
The result type decides how the expectation can continue: AndOrResult allows combining it with further expectations
using .And and .Or.
ExpectationBuilder
The next step is to extract the ExpectationBuilder. In order to keep the automatic code suggestions for developers
clear, IThat<TType> doesn't show it. The Get() extension method in the aweXpect.Core.Extending namespace gives
access to it. This namespace holds the helpers for extension authors that extend types every user sees, so they only
appear where you import it.
You can then use the ExpectationBuilder to add an IsRadioFriendlyConstraint:
using aweXpect.Core.Extending;
public static AndOrResult<Track, IThat<Track?>> IsRadioFriendly(this IThat<Track?> subject)
=> new(subject.Get().ExpectationBuilder.AddConstraint((it, grammars)
=> new IsRadioFriendlyConstraint(it, grammars)),
subject);
The factory receives the name of the subject (it) and the grammars of the sentence, which the constraint uses to
write its part of the failure message. Constraints and results shows how to write
the IsRadioFriendlyConstraint, and message conventions how its texts should read.