【发布时间】:2021-05-06 11:58:00
【问题描述】:
我最近开始使用 FluentAssertions,它应该有这个强大的对象图比较功能。
我正在尝试做最简单的事情:将Address 对象的属性与AddressDto 对象的属性进行比较。它们都包含 4 个简单的字符串属性:Country、City、Street 和 ZipCode(它不是生产系统)。
谁能给我解释一下,比如我两岁,怎么了?
partnerDto.Address.Should().BeEquivalentTo(partner.Address)
它失败并显示以下消息:
消息:
预期结果。地址为 4 Some street, 12345 Toronto, Canada, 但找到 AddressDto { Country = Canada, ZipCode = 12345, City = Toronto, Street = 4 Some street }。
有配置:
- 使用声明的类型和成员
- 按值比较枚举
- 按名称匹配成员(或抛出)
- 没有自动转换。
- 严格控制字节数组中的项目顺序
它似乎试图将Address 对象视为一个字符串(因为它覆盖了ToString()?)。我尝试使用options.ComparingByMembers<AddressDto>() 选项,但似乎没有任何区别。
(AddressDto 是 record,顺便说一句,不是 class,因为我正在用这个项目测试新的 .Net 5 功能;但它可能没什么区别。)
故事的寓意:
使用record而不是class会触发FluentAssertions,因为记录会在后台自动覆盖Equals(),并且FluentAssertions假定它应该使用Equals()而不是属性比较,因为覆盖的Equals()可能是为了提供所需的比较。
但是,在这种情况下,Equals() 在record 中的默认覆盖实现实际上仅在两种类型相同时才有效,因此它会失败,因此 FluentAssertions 在BeEquivalentTo() 上报告失败。
并且,在失败消息中,FluentAssertions 通过 ToString() 将对象转换为字符串,从而令人困惑地报告了该问题。这是因为记录具有“值语义”,因此它会这样对待它们。有一个open issue about this on GitHub。
我确认如果我将record 更改为class,则不会出现问题。
(我个人认为 FluentAssertions 在 record 上时应该忽略 Equals() 覆盖,并且两种类型不同,因为这种行为可以说不是人们所期望的。当时的当前问题发布时间,适用于 FluentAssertions 版本 5.10.3。)
我编辑了我的问题标题以更好地代表问题的实际情况,因此它可能对人们更有用。
参考资料:
正如人们所问的,这是域实体的定义(为了简洁起见,必须删除一些方法,因为我正在做 DDD,但它们肯定与问题无关):
public class Partner : MyEntity
{
[Required]
[StringLength(PartnerInvariants.NameMaxLength)]
public string Name { get; private set; }
[Required]
public Address Address { get; private set; }
public virtual IReadOnlyCollection<Transaction> Transactions => _transactions.AsReadOnly();
private List<Transaction> _transactions = new List<Transaction>();
private Partner()
{ }
public Partner(string name, Address address)
{
UpdateName(name);
UpdateAddress(address);
}
...
public void UpdateName(string value)
{
...
}
public void UpdateAddress(Address address)
{
...
}
...
}
public record Address
{
[Required, MinLength(1), MaxLength(100)]
public string Street { get; init; }
[Required, MinLength(1), MaxLength(100)]
public string City { get; init; }
// As I mentioned, it's not a production system :)
[Required, MinLength(1), MaxLength(100)]
public string Country { get; init; }
[Required, MinLength(1), MaxLength(100)]
public string ZipCode { get; init; }
private Address() { }
public Address(string street, string city, string country, string zipcode)
=> (Street, City, Country, ZipCode) = (street, city, country, zipcode);
public override string ToString()
=> $"{Street}, {ZipCode} {City}, {Country}";
}
这里是 Dto 等价物:
public record PartnerDetailsDto : IMapFrom<Partner>
{
public int Id { get; init; }
public string Name { get; init; }
public DateTime CreatedAt { get; init; }
public DateTime? LastModifiedAt { get; init; }
public AddressDto Address { get; init; }
public void Mapping(Profile profile)
{
profile.CreateMap<Partner, PartnerDetailsDto>();
profile.CreateMap<Address, AddressDto>();
}
public record AddressDto
{
public string Country { get; init; }
public string ZipCode { get; init; }
public string City { get; init; }
public string Street { get; init; }
}
}
【问题讨论】:
-
您能否在edit 的问题中包含
Address和AddressDto的定义? -
当然,@canton7;一会儿
-
partnerDto变量的类型是什么?Address属性是如何定义的? -
@Leaky 我们需要所有这些定义才能重现您的问题
-
仅供参考,关于这个github.com/fluentassertions/fluentassertions/issues/1451有一个未解决的问题
标签: c# .net-5 fluent-assertions