Related: #5703 (same pattern on Eventually/WaitsFor).
Problem
Member has specialised overloads for string and IEnumerable<T> members, but every other shape downcasts to generic IAssertionSource<TMember>. So on a Dictionary<K,V>, HashSet<T>, concrete List<T>, or any specialised type, the lambda cannot reach methods defined on the specialised source (DictionaryAssertionBase, SetAssertionBase, list-specific APIs, etc.).
Async Task<TMember> and ValueTask<TMember> overloads are also not specialised at all — even for string/IEnumerable returns. So Member(x => x.GetTagsAsync(), tags => tags.HasCount(3)) won't compile.
Repro
// Sync — Dictionary member, generic source only
await Assert.That(obj).Member(x => x.Map, m => m.ContainsKey(\"k\")); // fails: ContainsKey not on IAssertionSource<Dictionary<..>>
// Task<string> member — no string specialisation on Task overload
await Assert.That(obj).Member(x => x.NameAsync(), n => n.Contains(\"foo\")); // fails
// Task<IEnumerable<T>> — no collection specialisation on Task overload
await Assert.That(obj).Member(x => x.GetTagsAsync(), t => t.HasCount(3)); // fails
Affected overloads
TUnit.Assertions/Extensions/AssertionExtensions.cs:
:463 Member<TObject, TMember, TTransformed>(..., Expression<Func<TObject, TMember?>>, Func<IAssertionSource<TMember>, Assertion<TTransformed>>)
:504 Member<TObject, TMember>(..., Expression<Func<TObject, TMember?>>, Func<IAssertionSource<TMember>, Assertion<TMember>>)
:547 Member<TObject, TMember>(..., Expression<Func<TObject, TMember?>>, Func<IAssertionSource<TMember>, object>)
:590, :628, :667 Task variants — no specialisation
:726, :764, :803 ValueTask variants — no specialisation
Proposed fix
Add specialised overloads preserving the member source type:
Dictionary<K,V> / IDictionary<K,V> / IReadOnlyDictionary<K,V> → DictionaryAssertionBase-style source
HashSet<T> / ISet<T> → SetAssertionBase-style source
IList<T> / IReadOnlyList<T> / List<T> → list-specialised source
- Mirror
string + IEnumerable<T> specialisations onto Task<> and ValueTask<> overloads
Acceptance
- Specialised assertions on dictionary/set/list/string members usable inside
.Member(...) lambda without explicit casts.
- Applies to sync,
Task<>, and ValueTask<> forms.
- Tests cover each specialised member shape × each of (sync / Task / ValueTask).
Related: #5703 (same pattern on
Eventually/WaitsFor).Problem
Memberhas specialised overloads forstringandIEnumerable<T>members, but every other shape downcasts to genericIAssertionSource<TMember>. So on aDictionary<K,V>,HashSet<T>, concreteList<T>, or any specialised type, the lambda cannot reach methods defined on the specialised source (DictionaryAssertionBase,SetAssertionBase, list-specific APIs, etc.).Async
Task<TMember>andValueTask<TMember>overloads are also not specialised at all — even forstring/IEnumerablereturns. SoMember(x => x.GetTagsAsync(), tags => tags.HasCount(3))won't compile.Repro
Affected overloads
TUnit.Assertions/Extensions/AssertionExtensions.cs::463Member<TObject, TMember, TTransformed>(..., Expression<Func<TObject, TMember?>>, Func<IAssertionSource<TMember>, Assertion<TTransformed>>):504Member<TObject, TMember>(..., Expression<Func<TObject, TMember?>>, Func<IAssertionSource<TMember>, Assertion<TMember>>):547Member<TObject, TMember>(..., Expression<Func<TObject, TMember?>>, Func<IAssertionSource<TMember>, object>):590, :628, :667Task variants — no specialisation:726, :764, :803ValueTask variants — no specialisationProposed fix
Add specialised overloads preserving the member source type:
Dictionary<K,V>/IDictionary<K,V>/IReadOnlyDictionary<K,V>→DictionaryAssertionBase-style sourceHashSet<T>/ISet<T>→SetAssertionBase-style sourceIList<T>/IReadOnlyList<T>/List<T>→ list-specialised sourcestring+IEnumerable<T>specialisations ontoTask<>andValueTask<>overloadsAcceptance
.Member(...)lambda without explicit casts.Task<>, andValueTask<>forms.