Expectations for reflection types for aweXpect.
Write architecture and convention tests as plain, readable assertions: select the assemblies, types
or members you care about with In or Types, then assert a rule on them with Expect.That.
// "Every async method must end in 'Async'"awaitExpect.That(In.AssemblyContaining<MyClass>()// ① pick a source.Methods()// ② navigate to a member kind.WhichAreAsync())// ③ filter it down.HaveName("Async").AsSuffix();// ④ assertEvery expectation follows the same four-part shape:
| Step | What it does | Examples |
|---|---|---|
| ① Source | choose where to look | In.AllLoadedAssemblies(), In.AssemblyContaining<T>(), In.Type<T>() |
| ② Navigate | move to a member kind | .Types(), .Methods(), .Properties(), .Fields(), .Events(), .Constructors() |
| ③ Filter(optional) | narrow the set | .WhichArePublic(), .With<T>(), .WithName(…), .Which(…) |
| ④ Assert | state the rule | Expect.That(…).HaveName(…), .AreClasses(), .Return<Task>() |
Steps ② and ③ are optional. You can assert directly on a single Type, MethodInfo, … or on a whole
Assembly, and every expectation works the same whether the subject is one item or a collection
(Assembly[], IEnumerable<Type?>, …).
The supported reflection subjects are
Assembly,
Type,
ConstructorInfo,
EventInfo,
FieldInfo,
MethodInfo and
PropertyInfo.
// Verify all test classes (those containing a [Fact] or [Theory] method) follow the naming conventionawaitExpect.That(In.AllLoadedAssemblies().Public.Classes().WhichContainMethods(m =>m.With<FactAttribute>().OrWith<TheoryAttribute>())).HaveName("Tests").AsSuffix();// Verify all async methods have an "Async" suffixawaitExpect.That(In.AssemblyContaining<MyClass>().Methods().WhichAreAsync()).HaveName("Async").AsSuffix();// Verify all methods with an "Async" suffix return Task or ValueTaskawaitExpect.That(In.AssemblyContaining<MyClass>().Methods().WithName("Async").AsSuffix()).Return<Task>().OrReturn<ValueTask>();// Verify controllers follow the naming conventionawaitExpect.That(In.AllLoadedAssemblies().Types().WhichInheritFrom<ControllerBase>()).HaveName("Controller").AsSuffix();// Verify each event handler is named after the event it handles (e.g. "OnOrderPlaced")awaitExpect.That(In.AssemblyContaining<MyAggregate>().Methods().Which(m =>m.GetParameters().Length==1)).HaveName(method =>"On"+method.GetParameters()[0].ParameterType.Name);// Verify each serializable type has exactly one parameterless constructorawaitExpect.That(In.AllLoadedAssemblies().Types().With<SerializableAttribute>().WhichContainConstructors(c =>c.WithoutParameters()).Exactly(1)).AreClasses();Filters and assertions documents the complete filter and assertion vocabulary: access modifiers, attributes, names and namespaces, type kinds, methods (return types, parameters, async, operators, …), properties, fields, events, constructors and assemblies.
There is no separate rule engine: a "layer" is just a reusable type selection, and an architecture rule is
just an expectation on it. Combine several rules into a single verification with Expect.ThatAll:
Filtered.TypesdomainTypes=Types.InNamespace("MyApp.Domain");Filtered.TypesinfrastructureTypes=Types.InNamespace("MyApp.Infrastructure");awaitExpect.ThatAll(Expect.That(domainTypes).DoNotDependOn(infrastructureTypes),Expect.That(domainTypes).AreSealed());A failing rule reports all violations, numbered per expectation:
Expected all of the following to succeed:
[01] Expected that domainTypes all do not depend on types within namespace "MyApp.Infrastructure" in all loaded assemblies
[02] Expected that domainTypes are all sealed
but
[01] it contained types with the dependency [
OrderService
]
[02] it contained non-sealed types [
Order,
Invoice
]
The dependency rules (DependOn / DependOnlyOn / HaveDependenciesOutside), dependency cycle detection
(HaveNoDependencyCycles) and exemptions (Except) are documented in
Architecture rules.
The full documentation is available at docs.testably.org:
- Selecting types and members: the
InandTypessources and navigating between assemblies, types and members - Filters and assertions: the complete reference for all subject kinds, string matching options and quantifiers
- Architecture rules: type dependencies, dependency cycles and layers as type selections
- Configuration: assembly exclusions, compiler-generated members and the dependency resolver
- Comparisons: feature maps against FluentAssertions and against NetArchTest and ArchUnitNET