You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FluentAssertions:比较含同名不同类型属性的对象

Problem with FluentAssertions' BeEquivalentTo when using partial classes with interface properties alongside XSD-generated classes

I’ve run into exactly this same issue before, and the root cause boils down to how you’re implementing your interface in the partial classes. Let’s break down the solutions, starting with the cleanest fix:

Your current partial class code is accidentally adding duplicate properties (one from the XSD generator, one from your interface implementation) instead of mapping the interface to the existing generated properties. This creates two separate header/body properties on the class—one concrete, one interface-type—with data only stored in the latter.

Instead, use explicit interface implementation to map the interface properties directly to the generated ones. This way, there’s only one set of properties, and the interface just provides a unified way to access them:

// Keep your interface as-is
public interface IXsdGeneratedClass {
    IXsdGeneratedClassHeader header { get; set; }
    IXsdGeneratedClassBody body { get; set; }
}

// Corrected partial class implementation
public partial class xsdGeneratedClass1 : IXsdGeneratedClass {
    // Explicitly implement the interface's header property, pointing to the generated concrete property
    IXsdGeneratedClassHeader IXsdGeneratedClass.header {
        get => this.header;
        set => this.header = (xsdGeneratedClass1Header)value;
    }

    IXsdGeneratedClassBody IXsdGeneratedClass.body {
        get => this.body;
        set => this.body = (xsdGeneratedClass1Body)value;
    }
}

public partial class xsdGeneratedClass2 : IXsdGeneratedClass {
    IXsdGeneratedClassHeader IXsdGeneratedClass.header {
        get => this.header;
        set => this.header = (xsdGeneratedClass2Header)value;
    }

    IXsdGeneratedClassBody IXsdGeneratedClass.body {
        get => this.body;
        set => this.body = (xsdGeneratedClass2Body)value;
    }
}

With this change, all data lives in the auto-generated properties, and the interface just wraps them. FluentAssertions will now compare the actual data correctly using its default configuration—no extra setup needed.

2. Workaround If You Can’t Modify the Partial Class

If you’re stuck with the duplicate properties for some reason, you can configure FluentAssertions to ignore the concrete generated properties and only compare the interface-type ones:

Option A: Compare Only Interface Members

Use IncludingMembersImplementedIn to tell FluentAssertions to only consider properties defined by your interface:

// Example test code
var actual = new xsdGeneratedClass1
{
    header = new XsdGeneratedClass1HeaderImpl(), // Your interface implementation
    body = new XsdGeneratedClass1BodyImpl()
};

var expected = new xsdGeneratedClass1
{
    header = new XsdGeneratedClass1HeaderImpl { Id = "123" },
    body = new XsdGeneratedClass1BodyImpl { Data = "test" }
};

actual.Should().BeEquivalentTo(expected, options =>
    options.IncludingMembersImplementedIn<IXsdGeneratedClass>());

Option B: Exclude Concrete Generated Properties

If you need more control, explicitly exclude the auto-generated concrete properties:

actual.Should().BeEquivalentTo(expected, options =>
    options.Excluding(member => 
        member.Name == nameof(xsdGeneratedClass1.header) && 
        member.Type == typeof(xsdGeneratedClass1Header))
           .Excluding(member => 
        member.Name == nameof(xsdGeneratedClass1.body) && 
        member.Type == typeof(xsdGeneratedClass1Body)));

For reuse across multiple classes, wrap this in an extension method:

public static class FluentAssertionsExtensions
{
    public static EquivalencyAssertionOptions<T> ExcludeXsdConcreteProperties<T>(this EquivalencyAssertionOptions<T> options)
    {
        return options.Excluding(member => 
            (member.Name is "header" or "body") && 
            !member.Type.IsInterface);
    }
}

// Usage in tests
actual.Should().BeEquivalentTo(expected, options => options.ExcludeXsdConcreteProperties());

Why RespectingRuntimeTypes Didn’t Work

That option is designed for polymorphic type comparisons (e.g., when your expected type is a base class but the actual object is a derived class). It doesn’t solve the problem of duplicate properties with the same name but different types—hence why it didn’t fix your issue.

内容的提问来源于stack exchange,提问作者Dennis Stuhr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:06:16