Skip to content

Conversation

@jnyrup
Copy link
Member

@jnyrup jnyrup commented Apr 4, 2020

This PR enables using GenericDictionaryAssertions on IReadOnlyDictionary.
More precisely it enables support for any

TCollection : IEnumerable<KeyValuePair<TKey, TValue>>

Should analysis:

Added two new overloads:

Should<TKey, TValue>(IEnumerable<KeyValuePair<TKey, TValue>>)
Should<TCollection, TKey, TValue>(TCollection)
    where TCollection : IEnumerable<KeyValuePair<TKey, TValue>>

The first one is picked by both:

  • IEnumerable<KeyValuePair<TKey, TValue>>
  • IReadOnlyDictionary<TKey, TValue>

The second overload is not picked up by anything and probably never will(?), as being an interface is regarded a better match than being generic in an interface.
Related article
Thus not sure if having the second one is better/worse that not having it.

As noted in previous discussions we cannot add

Should(IReadOnlyDictionary<TKey, TValue>)

because that leads to ambiguity when writing

new Dictionary<int,int>().Should()

as it cannot tie-break picking IReadOnlyDictionary or IDictionary.

GenericCollectionAssertions inheritance

GenericDictionaryAssertions now inherits from GenericCollectionAssertions where it previously inherited from ReferenceTypeAssertions.
This has at least two benefits:
All Count/Empty assertions now comes for free.
Their failure messages were slightly different, which are the changes to GenericCollectionAssertions and failure message assertions in existing tests.

The only assertions for IEnumerable<kvp>.Should() that might be different
are [Not]Contain(KeyValuePair<TKey, TValue> as they are marked with new modifier.

[Not]Equal

I've made Equal and NotEqual such that they do not require the subject and expectation to be the same TCollection.
So an IDictionary can be compared against an IReadOnlyDictionary and vice versa.

What's up with all the helper methods?

I wanted to avoid littering the calls to e.g. GetValue in assertion bodies with type parameters.
I.e. I wanted to be able to write GetValues(Subject) instead of GetValues<TCollection, TKey, TValue>(Subject).
This way compiled and worked.
If there is a nicer way to accomplish this, I'm all eager to know.

Tests

I've added a set of tests to verify that all cases in all helper methods are hit.
I attached a debugger to verify that the expected cases were hit.
For a proper testing of this I would normally use a mock.

If we allowed to expose internal members to the test project it could also be tested with the existing TrackingTestDictionary class.
Any opinion/concerns?

A huge thanks to everyone who participated in the discussions over the years(!) on how to solve this.
@Evangelink @krajek @somewhatabstract

This fixes #568
This fixes #357
This closes #1132
This closes #963

Copy link
Member

@dennisdoomen dennisdoomen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Impressive work and lots of kudos for all the folks that contributed to this topic.

@jnyrup jnyrup merged commit 387bada into fluentassertions:develop Apr 5, 2020
@jnyrup jnyrup deleted the IReadOnlyDictionary branch April 5, 2020 10:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for IReadOnlyDictionary, IReadOnlyCollection IReadOnlyDictionary

4 participants