New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add the ability to exclude non-browsable members from equivalency tests #1807
Add the ability to exclude non-browsable members from equivalency tests #1807
Conversation
…. Implemented it in SelfReferenceEquivalencyAssertionOptions.cs and CollectionMemberAssertionOptionsDecorator.cs and UsersOfGetClosedGenericInterfaces.cs. Added property IsBrowsable to IMember.cs and implemented it in Field.cs and Property.cs. Adjusted the implementation of AssertMemberEquality in StructuralEqualityEquivalencyStep.cs to combine these fields to allow for non-browsable members to be skipped when checking equivalence. Added automated tests of the new functionality to SelectionRulesSpec.cs. Accepted API changes into the approved API.
…mbers exclusion feature. Updated releases.md to describe the new feature.
Src/FluentAssertions/Equivalency/SelfReferenceEquivalencyAssertionOptions.cs
Show resolved
Hide resolved
@@ -1489,5 +1490,89 @@ public void Including_an_interface_property_through_inheritance_should_work() | |||
.Including(a => a.Value2) | |||
.RespectingRuntimeTypes()); | |||
} | |||
|
|||
[Theory] | |||
[MemberData(nameof(TestMembers))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 I found it very hard to understand what this is doing with the parameter. Because of that, I strongly believe it is much better to drop the parameterized tests and have individual tests for the relevant test cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh? This is just how Xunit works. Maybe this can be improved with comments and/or better naming??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can continue using parameterized tests, but without using ApplyDifference()
to mutate b
.
public static object[][] TestMembers => new object[][]
{
new[] { new ClassWithNonBrowsableMembers { BrowsableField = 1 } },
new[] { new ClassWithNonBrowsableMembers { BrowsableProperty = 1 } },
new[] { new ClassWithNonBrowsableMembers { AdvancedBrowsableField = 1 } },
new[] { new ClassWithNonBrowsableMembers { AdvancedBrowsableProperty = 1 } },
new[] { new ClassWithNonBrowsableMembers { NonBrowsableField = 1 } },
new[] { new ClassWithNonBrowsableMembers { NonBrowsableProperty = 1 } }
};
[Theory]
[MemberData(nameof(TestMembers))]
public void Including_non_browsable_members_should_work(ClassWithNonBrowsableMembers b)
{
...
}
public class ClassWithNonBrowsableMembers
{
public int BrowsableField;
public int BrowsableProperty { get; set; }
[EditorBrowsable(EditorBrowsableState.Advanced)]
public int AdvancedBrowsableField;
[EditorBrowsable(EditorBrowsableState.Advanced)]
public int AdvancedBrowsableProperty { get; set; }
[EditorBrowsable(EditorBrowsableState.Never)]
public int NonBrowsableField;
[EditorBrowsable(EditorBrowsableState.Never)]
public int NonBrowsableProperty { get; set; }
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I strongly disagree. Parameterized tests are only useful if you have a simple list of values that you use in combination with a Theory
attribute. For anything else, they obscure the cause and effect of the test. In this case, having 6 different tests would be much clearer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh dear :-P Which way should I take it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting 🙃 I prefer the parameterized one to this, as I don't have to jump between method bodies.
Conditionals in tests is a (99.9%) no-go for me.
For parameterized tests I would create separate test case generators to avoid that.
Seems Dennis and I can agree on spelling out the entire test cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, conditionals are gone. :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, was trying to understand each test, but still had to dig through the private methods to understand what each test was doing. I really want them to be spelled out and have functional names.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have pushed a commit that gives the methods in question much more explicit names. Is that what you had in mind? :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately not. In fact, it makes it worse since you refer to the literal names of the APIs, which is an anti-pattern for me. What I meant is to drop the parameterized tests completely.
@@ -1489,5 +1490,89 @@ public void Including_an_interface_property_through_inheritance_should_work() | |||
.Including(a => a.Value2) | |||
.RespectingRuntimeTypes()); | |||
} | |||
|
|||
[Theory] | |||
[MemberData(nameof(TestMembers))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can continue using parameterized tests, but without using ApplyDifference()
to mutate b
.
public static object[][] TestMembers => new object[][]
{
new[] { new ClassWithNonBrowsableMembers { BrowsableField = 1 } },
new[] { new ClassWithNonBrowsableMembers { BrowsableProperty = 1 } },
new[] { new ClassWithNonBrowsableMembers { AdvancedBrowsableField = 1 } },
new[] { new ClassWithNonBrowsableMembers { AdvancedBrowsableProperty = 1 } },
new[] { new ClassWithNonBrowsableMembers { NonBrowsableField = 1 } },
new[] { new ClassWithNonBrowsableMembers { NonBrowsableProperty = 1 } }
};
[Theory]
[MemberData(nameof(TestMembers))]
public void Including_non_browsable_members_should_work(ClassWithNonBrowsableMembers b)
{
...
}
public class ClassWithNonBrowsableMembers
{
public int BrowsableField;
public int BrowsableProperty { get; set; }
[EditorBrowsable(EditorBrowsableState.Advanced)]
public int AdvancedBrowsableField;
[EditorBrowsable(EditorBrowsableState.Advanced)]
public int AdvancedBrowsableProperty { get; set; }
[EditorBrowsable(EditorBrowsableState.Never)]
public int NonBrowsableField;
[EditorBrowsable(EditorBrowsableState.Never)]
public int NonBrowsableProperty { get; set; }
}
Src/FluentAssertions/Equivalency/Steps/StructuralEqualityEquivalencyStep.cs
Outdated
Show resolved
Hide resolved
Swapped the order of operands in StructuralEqualityEquivalencyStep so that it only queries matchingMember.IsBrowsable if it could make a difference. Updated the implementations of IsBrowsable in Field.cs and Property.cs to use lazy evaluation, and to use pattern matching to be more concise and readable.
…n while also avoiding conditionals.
@@ -1493,86 +1493,131 @@ public void Including_an_interface_property_through_inheritance_should_work() | |||
|
|||
[Fact] | |||
public void Including_non_browsable_members_when_testing_NotEquivalent_should_work_when_browsable_field_differs() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Almost ready...
We need to avoid to use the literal name of APIs like NotBeEquivalentTo
and BeEquivalentTo
. In this case, I would focus the scenarios using BeEquivalentTo
because that's what NotBeEquivalentTo
internally uses. E.g. Browsable_fields_can_be_included_in_the_assertion
Also, I would explicitly show the value of BrowsableField
of the subject.
Also applies on the other scenarios.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean by
explicitly show the value of
BrowsableField
of the subject
?? The test should write it to test output??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Fact]
public void When_browsable_field_differs_including_non_browsable_members_should_not_affect_result()
{
// Arrange
var subject = new ClassWithNonBrowsableMembers() { BrowsableField = 2 };
var expectation = new ClassWithNonBrowsableMembers() { BrowsableField = 1 };
// Act
Action action =
() => subject.Should().BeEquivalentTo(expectation, config => config.IncludingNonBrowsableMembers());
// Assert
action.Should().Throw<XunitException>();
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh, okay, don't use the implicit value of 0. Got it :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've pushed a commit to this effect. It explicitly assigns to the member where a difference is being set up for both the subject and the expectation now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh, okay, don't use the implicit value of 0. Got it :-)
Exactly, because you don't know that if you look at the test. In other words, if it's important to understand the test case, it's very important to show it ;-)
fc796b2
to
fb9f459
Compare
…implementation. Reworked tests of IncludingNonBrowsableMembers and ExcludingNonBrowsableMembers in SelectionRulesSpecs to all test BeEquivalentTo, and renamed them per input from @dennisdoomen. Updated tests of IncludingNonBrowsableMembers and ExcludingNonBrowsableMembers to not just rely on default values of any of the members being tested.
fb9f459
to
3dd47f0
Compare
…citly marked as being always browsable, in addition to testing members that are in this state by default.
…and Property.cs. Corrected a number of tests in SelectionRulesSpec.cs that used the wrong configuration method for the test.
The automated PR build failed. It looks like a spurious failure to me. I don't seem to have access to the "Re-run" functionality to make it give it another go. |
..but a merge conflict on |
Pull Request Test Coverage Report for Build 1866102890
💛 - Coveralls |
Thanks for the great work. |
…ency tests (fluentassertions#1807)" This reverts commit 391519b.
This PR:
ExcludeNonBrowsable
toIEquivalencyAssertionOptions
, and implements it inSelfReferenceEquivalencyAssertionOptions
,CollectionMemberAssertionOptionsDecorator
andUsersOfGetClosedGenericInterfaces
.IsBrowsable
toIMember
and implements it inField
andProperty
.AssertMemberEquality
inStructuralEqualityEquivalencyStep
to allow for non-browsable members to be skipped when checking equivalence.SelectionRulesSpec
.IMPORTANT