Skip to content

WithMapping does not map properties of collections #1840

@drewkill32

Description

@drewkill32

Description

When using BeEquivalentTo and WithMapping over a collection the properties are not mapped and are ignored.

Complete minimal example reproducing the issue

using FluentAssertions;
using FluentAssertions.Equivalency.Tracing;


var entity = new Entity
{
    Name = "Test",
    EntityId = 1
};

var dto = EntityDto.MapFromEntity(entity);


var entityCol = new[] { entity };
var dtoCol = new[] { dto };



dtoCol.Should().BeEquivalentTo(entityCol, c =>
    c.WithMapping<EntityDto>(s => s.EntityId, d => d.Id)
        .WithTracing());

//     FluentAssertions.Execution.AssertionFailedException: Expectation has property dtoCol[0].EntityId that the other object does not have.
//
//     With configuration:
//     - Use declared types and members
//     - Compare enums by value
//     - Compare tuples by their properties
//     - Compare anonymous types by their properties
//     - Compare records by their members
//     - Include all non-private properties
//     - Include all non-private fields
//     - FluentAssertions.Equivalency.Matching.MappedPathMatchingRule
//     - Match member by name (or throw)
//     - Be strict about the order of items in byte arrays
//     - Without automatic conversion.
//
//     With trace:
//       dtoCol
//       {
//         Structurally comparing System.Object[] and expectation Entity[] at dtoCol
//         {
//           Finding the best match of Entity within all items in System.Object[] at dtoCol[0]
//           {
//             Comparing subject at dtoCol[0] with the expectation at dtoCol[0]
//             {
//               dtoCol[0]
//               {
//                 property dtoCol[0].Name
//                 {
//                   Equivalency was proven by ReferenceEqualityEquivalencyStep
//                 }
//                 Equivalency was proven by StructuralEqualityEquivalencyStep
//               }
//               Contained 1 failures
//             }
//           }
//         }
//         Equivalency was proven by GenericEnumerableEquivalencyStep
//       }
//
//        at FluentAssertions.Execution.FallbackTestFramework.Throw(String message)
//        at FluentAssertions.Execution.TestFrameworkProvider.Throw(String message)
//        at FluentAssertions.Execution.CollectingAssertionStrategy.ThrowIfAny(IDictionary`2 context)
//        at FluentAssertions.Execution.AssertionScope.Dispose()
//        at FluentAssertions.Equivalency.EquivalencyValidator.AssertEquality(Comparands comparands, EquivalencyValidationContext context)
//        at FluentAssertions.Collections.GenericCollectionAssertions`3.BeEquivalentTo[TExpectation](IEnumerable`1 expectation, Func`2 config, String because, Object[] becauseArgs)

dto.Should().BeEquivalentTo(entity, c => c
        .WithMapping<EntityDto>(s => s.EntityId, d => d.Id)
        .WithTracing());

//    FluentAssertions.Execution.AssertionFailedException: Expected property dto.Id to be 1, but found 0.

//    With configuration:
//    - Use declared types and members
//    - Compare enums by value
//    - Compare tuples by their properties
//    - Compare anonymous types by their properties
//    - Compare records by their members
//    - FluentAssertions.Equivalency.Matching.MappedPathMatchingRule
//    - Match member by name (or throw)
//    - Be strict about the order of items in byte arrays
//    - Without automatic conversion.
//   
//    With trace:
//      dto
//      {
//          property dto.Id
//          {
//              Treating property dto.Id as a value type because System.Int32 overrides Equals.
//                  Equivalency was proven by ValueTypeEquivalencyStep
//          }
//          property dto.Name
//          {
//              Equivalency was proven by ReferenceEqualityEquivalencyStep
//          }
//          Equivalency was proven by StructuralEqualityEquivalencyStep
//      }
//   
//      at FluentAssertions.Execution.FallbackTestFramework.Throw(String message)
//      at FluentAssertions.Execution.TestFrameworkProvider.Throw(String message)
//      at FluentAssertions.Execution.CollectingAssertionStrategy.ThrowIfAny(IDictionary`2 context)
//      at FluentAssertions.Execution.AssertionScope.Dispose()
//      at FluentAssertions.Equivalency.EquivalencyValidator.AssertEquality(Comparands comparands, EquivalencyValidationContext context)
//      at FluentAssertions.Primitives.ObjectAssertions`2.BeEquivalentTo[TExpectation](TExpectation expectation, Func`2 config, String because, Object[] becauseArgs)


public class Entity
{
    public int EntityId { get; set; }
    public string Name { get; set; }
}

public class EntityDto 
{
    public int Id { get; set; }
    public string Name { get; set; }

    public static EntityDto MapFromEntity(Entity entity)
    {
        return new EntityDto()
        {
            Name = entity.Name
        };
    }
}

Expected behavior:

I would expect the same behavior using WithMapping over a collection as I would with a single object

Actual behavior:

FluentAssertions does not map the property. If you use ExcludingMissingMembers then the collection assertion incorrectly passes.

Versions

Fluent Assertions 6.5.1
dotnet version 6.0.102

Additional Information

possible related issue #1838

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions