Skip to content
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

Excluding not functioning on overriden linked property #1077

Closed
lopen139 opened this issue Jun 7, 2019 · 5 comments
Closed

Excluding not functioning on overriden linked property #1077

lopen139 opened this issue Jun 7, 2019 · 5 comments
Labels

Comments

@lopen139
Copy link

@lopen139 lopen139 commented Jun 7, 2019

Description

When trying to apply an exclusion to a property that links to a different property, Fluent Assertions ignores the exclusion on said property, saying the test failed when

Complete minimal example reproducing the issue

Test method:

[TestMethod]
public async Task Then()
{
	var obj1 = new Candidate{PersonalInfo = new CandidatePersonalInfo{Firstname = "John", Lastname = "Johnson", Initials = "JJ"}};
	var obj2 = new Candidate{PersonalInfo = new CandidatePersonalInfo{Firstname = "John", Lastname = "Jameson", Initials = "JJ"}};

	obj1.Should().BeEquivalentTo(obj2, opt => opt.Excluding(o => o.PersonalInfo.Fullname).Excluding(o => o.DocumentName).Excluding(o => o.PersonalInfo.Lastname));
}

Relevant objects:

public class TestCandidate : TestBase
{
	public override string DocumentName => PersonalInfo.Fullname;
	public CandidatePersonalInfo PersonalInfo;
}

public abstract class TestBase
{
	public abstract string DocumentName { get; }
}

public class CandidatePersonalInfo
{
	public CandidatePersonalInfo()
	{
		Location = new AddressInfo();
	}

	public string Firstname { get; set; }

	public string Middlename { get; set; }

	public string Lastname { get; set; }
			
	public string Initials { get; set; }

	public Gender Gender { get; set; }

	public DateTimeOffset? DateOfBirth { get; set; }

	public string Fullname => ($"{Firstname} {Middlename}".Trim() + $" {Lastname}").Trim();

}

Expected behavior:

The property DocumentName should be excluded from the assertion

Actual behavior:

The test case fails because the properties DocumentName are not equal

Then Failed: Expected member DocumentName to be 
Expected member DocumentName to be 
"John Jameson", but 
"John Johnson" differs near "ohn" (index 6).
Expected member Info.CreatedAt to be <2019-06-07 13:31:57.077648 +2h>, but found <2019-06-07 13:31:57.0770104 +2h>.

With configuration:
- Use declared types and members
- Compare enums by value
- Exclude member root.PersonalInfo.Fullname
- Exclude member root.DocumentName
- Exclude member root.PersonalInfo.Lastname
- Match member by name (or throw)
- Without automatic conversion.
- Be strict about the order of items in byte arrays

   at FluentAssertions.Execution.LateBoundTestFramework.Throw(String message) in C:\projects\fluentassertions-vf06b\Src\FluentAssertions\Execution\LateBoundTestFramework.cs:line 16
   at FluentAssertions.Execution.CollectingAssertionStrategy.ThrowIfAny(IDictionary`2 context) in C:\projects\fluentassertions-vf06b\Src\FluentAssertions\Execution\CollectingAssertionStrategy.cs:line 49
   at FluentAssertions.Equivalency.EquivalencyValidator.AssertEquality(EquivalencyValidationContext context) in C:\projects\fluentassertions-vf06b\Src\FluentAssertions\Equivalency\EquivalencyValidator.cs:line 45
   at RecruitNow.Cockpit.IntegrationTests.Candidates.GivenCandidatesController.WhenManagingCandidates.Then() in C:\Projects\RecruitNow.Cockpit 2.0\RecruitNow.Cockpit.IntegrationTests\Candidates\GivenCandidatesController\WhenManagingCandidates.cs:line 149


Versions

  • Which version of Fluent Assertions are you using?
    5.6.0
  • Which .NET runtime and version are you targeting? E.g. .NET framework 4.6.1 or .NET Core 2.0.
    .NET core 2.0

Additional Information

Any additional information, configuration or data that might be necessary to reproduce the issue.

@jnyrup jnyrup added the bug label Jun 10, 2019
@jnyrup
Copy link
Member

@jnyrup jnyrup commented Jun 10, 2019

Thanks for reporting this and attaching a test case.
I can confirm that there is a bug.

GetMemberPath() resolves o => o.DocumentName to Base.DocumentName

GetNonPrivateProperties() returns an array containing:

Derived.FirstName
Derived.LastName
Derived.DocumentName

So in ExcludeMemberByPathSelectionRule.OnSelectMembers it tries to exclude Base.DocumentName from selectedMembers which contains Derived.DocumentName.

I haven't thought the following through only tried it out.
This test passed and no existing test in the test suite broke, when I change

declaringTypes.Add(memberExpression.Member.DeclaringType);

to

declaringTypes.Add(node.Type);

Here's a more reduced test case.

public class Derived : Base
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    public override string DocumentName => $"{FirstName} {LastName}";
}

public abstract class Base
{
    public abstract string DocumentName { get; }
}

[Fact]
public void Test()
{
    var obj1 = new Derived { FirstName = "John", LastName = "Johnson" };
    var obj2 = new Derived { FirstName = "John", LastName = "Jameson" };

    obj1.Should().BeEquivalentTo(obj2, opt => opt
        .Excluding(o => o.DocumentName)
        .Excluding(o => o.LastName));
}

Loading

Evangelink added a commit to Evangelink/FluentAssertions that referenced this issue Jun 24, 2019
Evangelink added a commit to Evangelink/FluentAssertions that referenced this issue Jun 24, 2019
@jnyrup
Copy link
Member

@jnyrup jnyrup commented Aug 10, 2019

Just a heads up: 5.8.0 has been released with the fix for this.

Loading

@taylorn16
Copy link

@taylorn16 taylorn16 commented May 2, 2021

I think this issue may need to be re-opened. I am experiencing similar (unexpected) behavior.

If I paste the following snippet into dotnetfiddle, I get the output below: (sorry for the poor formatting)

using FluentAssertions;

public class BaseProp
{
    public string BaseVal { get; set; }
}

public class DerivedProp : BaseProp
{
    public string DerivedVal { get; set; }
}

public abstract class Base
{
    public abstract BaseProp Prop { get; }
}

public sealed class Derived : Base
{
    public override DerivedProp Prop { get; }
    public string OtherProp { get; set; }
    
    public Derived(DerivedProp prop)
    {
        Prop = prop;
    }
}

public class Program
{
    public static void Main()
    {
        var a = new Derived(new DerivedProp { DerivedVal = "a", BaseVal = "a_base" })
        {
            OtherProp = "other"
        };
        var b = new Derived(new DerivedProp { DerivedVal = "b", BaseVal = "b_base" })
        {
	        OtherProp = "other"
        };
        
        a.Should().BeEquivalentTo(b, opts => opts
                                            .ComparingByMembers<Derived>()
                                            .Excluding(d => d.Prop));
    }
}

Output:

Unhandled exception. FluentAssertions.Execution.AssertionFailedException: Expected member Prop.BaseVal to be "b_base", but "a_base" differs near "a_b" (index 0).

With configuration:
- Use declared types and members
- Compare enums by value
- Exclude member root.Prop
- Match member by name (or throw)
- Without automatic conversion.
- Be strict about the order of items in byte arrays

   at FluentAssertions.Execution.FallbackTestFramework.Throw(String message) in C:\projects\fluentassertions-vf06b\Src\FluentAssertions\Execution\FallbackTestFramework.cs:line 18
   at FluentAssertions.Execution.TestFrameworkProvider.Throw(String message) in C:\projects\fluentassertions-vf06b\Src\FluentAssertions\Execution\TestFrameworkProvider.cs:line 40
   at FluentAssertions.Execution.CollectingAssertionStrategy.ThrowIfAny(IDictionary`2 context) in C:\projects\fluentassertions-vf06b\Src\FluentAssertions\Execution\CollectingAssertionStrategy.cs:line 44
   at FluentAssertions.Equivalency.EquivalencyValidator.AssertEquality(EquivalencyValidationContext context) in C:\projects\fluentassertions-vf06b\Src\FluentAssertions\Equivalency\EquivalencyValidator.cs:line 43
   at FluentAssertions.Primitives.ObjectAssertions.BeEquivalentTo[TExpectation](TExpectation expectation, Func`2 config, String because, Object[] becauseArgs) in C:\projects\fluentassertions-vf06b\Src\FluentAssertions\Primitives\ObjectAssertions.cs:line 125
   at Program.Main()
Command terminated by signal 6

In this case, I am explicitly telling FluentAssertions to compare by members and exclude .Prop from any comparison of the Derived class. The error then, is unexpected, because it throws due to the values of .Prop not being equivalent.

Perhaps there was a regression between 5.8.0 and 5.10.3?

Loading

@dennisdoomen
Copy link
Member

@dennisdoomen dennisdoomen commented May 3, 2021

Hmm, I can reproduce this as well. But as soon as I remove Prop from Base, it succeeds. So it's something with the covariant property declaration that's causing issues. I suspect that this looks like the same problem that was reported in this issue, but in reality it's a different issue. @taylorn16 would you mind creating a new issue for this?

Loading

@taylorn16
Copy link

@taylorn16 taylorn16 commented May 3, 2021

Loading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

4 participants