Skip to main content

Feature Comparison with FluentAssertions

aweXpect.Reflection and FluentAssertions both assert against reflection types, with different APIs and feature sets. This page maps what each offers for assemblies, types, and members.

Conceptual model

FluentAssertions extends each reflection object with a synchronous subject.Should() entry point and reaches collections through selectors such as assembly.Types() or type.Methods(). aweXpect.Reflection wraps the same objects in an asynchronous await Expect.That(subject) call and builds collections from In.* sources (In.AllLoadedAssemblies(), In.AssemblyContaining<T>(), …) that are narrowed with WhichAre*/With* filter chains. aweXpect.Reflection also offers symmetric singular (Is*/Has*) and plural (Are*/Have*) assertions for every target, while FluentAssertions pairs single-subject assertions with a smaller set of selector assertions.

Coverage overview

Legend: ✅ dedicated assertion · ⚠️ only via a general mechanism (selector, string assertion, or LINQ) · ❌ no dedicated assertion.

CapabilityFluentAssertionsaweXpect.Reflection
Assembly: references / dependencies
Assembly: name⚠️
Assembly: version
Assembly: strong name / signing
Assembly: target framework
Type: kind (class/interface/enum/struct/record/…)⚠️
Type: abstract / sealed / static
Type: access modifier
Type: assignable / implement / derive
Type: namespace⚠️
Type: name⚠️
Type: attributes (incl. predicate)
Type: member presence by signature⚠️
Type: operator presence (by kind)⚠️
Type: conversion operators by signature
Type: immutability / member nullability
Type: dependencies (on / only on / outside an allowed set)
Method: virtual / async / return type
Method: static / abstract / sealed / generic / extension / operator
Method: override
Method: parameter modifiers / types / counts
Method: access modifier
Method: name⚠️
Property: readable / writable
Property: virtual
Property: static / abstract / required / indexer / init-setter / override
Property: nullable
Property: return / declared type
Constructor: dedicated assertions / filtering⚠️
Field: assertions (static / read-only / constant / required / nullable)
Event: assertions

The sections below detail each target with side-by-side examples.

Assembly

  • FluentAssertions (AssemblyAssertions): Reference/NotReference, BeSignedWithPublicKey/BeUnsigned, DefineType. No assertion for assembly name, version, or target framework; use the string API on assembly.GetName().
  • aweXpect.Reflection: HasName, HasVersion, DependsOn/DoesNotDependOn/DependsOnlyOn, IsStrongNamed, Targets, Has<TAttribute>; plural equivalents (HaveName, …) over collections.
assembly.Should().Reference(typeof(string).Assembly);
assembly.Should().NotReference(unwantedAssembly);
assembly.Should().BeSignedWithPublicKey("0024000004800000...");
assembly.GetName().Name.Should().Be("MyAssembly"); // name via string API

Type

  • FluentAssertions (TypeAssertions): Be<T>/NotBe<T>, BeAssignableTo<T>, Implement<T>/NotImplement<T>, BeDerivedFrom<T>, BeAbstract/BeSealed/BeStatic, HaveAccessModifier(CSharpAccessModifier), BeDecoratedWith<T> (with attribute predicate and OrInherit variants), member presence (HaveProperty/HaveMethod/HaveConstructor/HaveIndexer/HaveExplicit*), and conversion operators. Type kind is available only as a selector filter (ThatAreClasses()), and namespace only on the selector (BeInNamespace); a single type's name/namespace use the string API.
  • aweXpect.Reflection: type-kind assertions (IsAClass, IsAnInterface, IsAnEnum, IsAStruct, IsARecord, IsARecordStruct, IsARefStruct, IsADelegate, IsAnAttribute, IsAnException), IsAbstract/IsSealed/IsStatic/IsReadOnly/IsNested/IsGeneric/IsInstantiable/IsImmutable, access modifiers (IsPublic, …), InheritsFrom<T>().Directly(), HasName, HasNamespace/IsWithinNamespace, Has<TAttribute>, quantified member containment (ContainsMethods(), …), member nullability (OnlyHasNullableMembers/OnlyHasNonNullableMembers), type-level dependencies (DependsOn/DoesNotDependOn/DependsOnlyOn/HasDependenciesOutside, against namespaces or type selections), operator presence by kind (HasOperator(Operator), HasOperator<TOperand>(Operator)) and conversion operators by signature (HasImplicitConversionOperator<TSource, TTarget>, HasExplicitConversionOperator<TSource, TTarget>).
typeof(MyClass).Should().BeAssignableTo<IMyInterface>();
typeof(MyClass).Should().BeDerivedFrom<BaseClass>();
typeof(AbstractClass).Should().BeAbstract();
typeof(MyClass).Should().HaveAccessModifier(CSharpAccessModifier.Public);
typeof(MyClass).Should().BeDecoratedWith<MyAttribute>(a => a.Value == 3);
typeof(MyClass).Namespace.Should().Be("MyNamespace"); // namespace via string API

// type kind only as a selector filter:
AllTypes.From(assembly).ThatAreClasses().Should().BeInNamespace("MyNamespace");

Method

  • FluentAssertions (MethodInfoAssertions): BeVirtual/NotBeVirtual, BeAsync/NotBeAsync, ReturnVoid/NotReturnVoid, Return<T>/NotReturn<T>, HaveAccessModifier(CSharpAccessModifier), BeDecoratedWith<T>. No assertions for static/abstract/sealed/generic/extension/operator/override, no parameter assertions, and no name assertion (use the string API on method.Name).
  • aweXpect.Reflection: the above plus IsStatic/IsAbstract/IsSealed/IsGeneric/IsAnExtensionMethod/IsAnOperator (incl. a specific Operator, e.g. IsAnOperator(Operator.Equality)), Overrides<T>, ReturnsExactly<T>, HasName, and a parameter family: HasParameter<T>/HasParameterCount/HasInParameter/HasOutParameter/HasRefParameter/HasOptionalParameter/HasParamsParameter (with AtIndex/FromEnd/default-value refinements).
MethodInfo method = typeof(MyClass).GetMethod("MyMethod");
method.Should().BeVirtual();
method.Should().Return<Task>();
method.Should().HaveAccessModifier(CSharpAccessModifier.Public);
method.Should().BeDecoratedWith<ObsoleteAttribute>();
method.Name.Should().StartWith("My"); // name via string API

Property

  • FluentAssertions (PropertyInfoAssertions): BeVirtual/NotBeVirtual, BeReadable/BeWritable (each with an optional CSharpAccessModifier), NotBeReadable/NotBeWritable, Return<T>/NotReturn<T>, BeDecoratedWith<T>. No assertions for static/abstract/sealed/required/indexer/init-setter/override, and no name assertion.
  • aweXpect.Reflection: IsReadable/IsWritable/IsReadOnly/IsWriteOnly/IsReadWrite, HasAGetter/HasASetter/HasAnInitSetter, IsStatic/IsAbstract/IsSealed/IsVirtual, IsRequired, IsNullable, IsAnIndexer, IsAnExtensionProperty, Overrides<T>, IsOfType<T>/IsOfExactType<T>, HasName, Has<TAttribute>.
PropertyInfo property = typeof(MyClass).GetProperty("MyProperty");
property.Should().BeReadable(CSharpAccessModifier.Public);
property.Should().BeWritable();
property.Should().Return<int>();

Constructor

  • FluentAssertions: no dedicated constructor selector. A constructor is reached through Type.HaveConstructor(parameterTypes) / HaveDefaultConstructor(), after which .Which exposes the ConstructorInfo for HaveAccessModifier / BeDecoratedWith.
  • aweXpect.Reflection: dedicated singular (ThatConstructor) and plural (ThatConstructors) assertions and an In.*…Constructors() filter: IsStatic, the full parameter family (HasParameter<T>, HasParameterCount, HasInParameter/HasOutParameter/HasRefParameter/HasOptionalParameter/HasParamsParameter), Has<TAttribute>.
typeof(MyClass).Should().HaveConstructor(new[] { typeof(int) })
.Which.Should().HaveAccessModifier(CSharpAccessModifier.Public);
typeof(MyClass).Should().HaveDefaultConstructor();

Field

  • FluentAssertions: no FieldInfo assertions.
  • aweXpect.Reflection: singular (ThatField) and plural (ThatFields) assertions and an In.*…Fields() filter: IsStatic, IsReadOnly, IsConstant, IsRequired, IsNullable, IsOfType<T>/IsOfExactType<T>, access modifiers (IsPublic, …), HasName, Has<TAttribute>.
// No dedicated FieldInfo assertions; fall back to raw reflection + LINQ/string assertions:
typeof(MyClass).GetField("_counter", BindingFlags.Instance | BindingFlags.NonPublic)
.IsInitOnly.Should().BeTrue();

Event

  • FluentAssertions: no EventInfo assertions.
  • aweXpect.Reflection: singular (ThatEvent) and plural (ThatEvents) assertions and an In.*…Events() filter: IsStatic, IsAbstract, IsSealed, IsOfType<T>/IsOfExactType<T>, access modifiers, HasName, Has<TAttribute>.
// No dedicated EventInfo assertions; fall back to raw reflection:
typeof(MyClass).GetEvent("Changed").Should().NotBeNull();

Selecting and filtering sets

  • FluentAssertions: build a set with assembly.Types(), AllTypes.From(assembly), type.Methods(), type.Properties(), then narrow with selector methods (ThatAreClasses(), ThatDeriveFrom<T>(), ThatAreDecoratedWith<T>(), ThatAreInNamespace(), ThatReturn<T>(), ThatArePublicOrInternal, ThatSatisfy(predicate), …) and assert with the selector assertions. There are no FieldInfo/EventInfo selectors and no ConstructorInfo selector.
  • aweXpect.Reflection: start from an In.* source, then chain WhichAre*/With<T>/WithName/Without* filters (with Or* combinations and quantifiers) across every member kind, and assert with the plural Are*/Have* methods.
AllTypes.From(assembly)
.ThatAreClasses()
.ThatAreDecoratedWith<TestFixtureAttribute>()
.Should().BeInNamespace("MyProject.Tests");

Name and string matching

  • FluentAssertions: reflection assertions have no built-in name matching. Names are compared with the string API (type.Name.Should().StartWith(…)) or a LINQ predicate on a selector.
  • aweXpect.Reflection: every name/namespace/dependency comparison exposes string options: AsPrefix(), AsSuffix(), AsRegex(), AsWildcard(), IgnoringCase(), Using(comparer).
type.Name.Should().EndWith("Tests");