Skip to content

About

Expectations for reflection types

Resources

Code of conduct

Contributing

Stars

2 stars

Watchers

1 watching

Forks

Latest commit

 

History

283 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

aweXpect.Reflection

Nuget Build Quality Gate Status Coverage Mutation testing badge

Expectations for reflection types for aweXpect.

At a glance

Write architecture and convention tests as plain, readable expectations: select the assemblies, types or members you care about with In or Types, then expect a rule on them with Expect.That.

// "Every async method must end in 'Async'"
await Expect.That(In.AssemblyContaining<MyClass>()    // ① pick a source
        .Methods()                                    // ② navigate to a member kind
        .WhichAreAsync())                             // ③ filter it down
    .HaveName("Async").AsSuffix();                    // ④ expect

Every 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(…)
④ Expect state the rule Expect.That(…).HaveName(…), .AreClasses(), .Return<Task>()

Steps ② and ③ are optional. You can verify a single Type, MethodInfo, … or a whole Assembly directly, 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.

Real-world examples

// Verify all test classes (those containing a [Fact] or [Theory] method) follow the naming convention
await Expect.That(In.AllLoadedAssemblies()
        .Public.Classes()
        .WhichContainMethods(m => m.With<FactAttribute>().OrWith<TheoryAttribute>()))
    .HaveName("Tests").AsSuffix();

// Verify all async methods have an "Async" suffix
await Expect.That(In.AssemblyContaining<MyClass>()
        .Methods().WhichAreAsync())
    .HaveName("Async").AsSuffix();

// Verify all methods with an "Async" suffix return Task or ValueTask
await Expect.That(In.AssemblyContaining<MyClass>()
        .Methods().WithName("Async").AsSuffix())
    .Return<Task>().OrReturn<ValueTask>();

// Verify controllers follow the naming convention
await Expect.That(In.AllLoadedAssemblies()
        .Types().WhichInheritFrom<ControllerBase>())
    .HaveName("Controller").AsSuffix();

// Verify each event handler is named after the event it handles (e.g. "OnOrderPlaced")
await Expect.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 constructor
await Expect.That(In.AllLoadedAssemblies()
        .Types().With<SerializableAttribute>()
        .WhichContainConstructors(c => c.WithoutParameters()).Exactly(1))
    .AreClasses();

Filters and expectations documents the complete filter and expectation vocabulary: access modifiers, attributes, names and namespaces, type kinds, methods (return types, parameters, async, operators, …), properties, fields, events, constructors and assemblies.

Architecture rules

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.Types domainTypes         = Types.InNamespace("MusicStore.Domain");
Filtered.Types infrastructureTypes = Types.InNamespace("MusicStore.Infrastructure");

await Expect.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 types within namespace "MusicStore.Domain" in all loaded assemblies all do not depend on types within namespace "MusicStore.Infrastructure" in all loaded assemblies
 [02] Expected that types within namespace "MusicStore.Domain" in all loaded assemblies are all sealed
but
 [01] it contained types with the dependency
 [02] it contained non-sealed types

[01] Not matching items:
[
  PlaylistService
]

[02] Not matching items:
[
  Album,
  Track
]

The dependency rules (DependOn / DependOnlyOn / HaveDependenciesOutside), dependency cycle detection (HaveNoDependencyCycles) and exemptions (Except) are documented in Architecture rules.

Documentation

The full documentation is available at docs.testably.org:

About

Expectations for reflection types

Resources

Code of conduct

Contributing

Stars

2 stars

Watchers

1 watching

Forks

Used by

Contributors

Languages