使用 FluentAssertions 比较包含不同类型的两个字典集合
Posted
技术标签:
【中文标题】使用 FluentAssertions 比较包含不同类型的两个字典集合【英文标题】:Using FluentAssertions to compare two collections of dictionaries that are containing different types 【发布时间】:2018-09-25 19:08:08 【问题描述】:从我们的数据库中,我们查询放入ExpandoObject
类型的动态对象集合中的记录集,这些对象在字段上实现IDictionary<string, object>
。这些是实际值。
从我们的 SpecFlow BDD 测试中,我们获得了实现 IDictionary<string, string>
的 TableRows 集合。这些是我们的预期值。
使用 FluentAssertions,我们希望使用 actual.Should().BeEquivalentTo(expected)
测试整个集合的等效性。不幸的是,这不起作用,因为当实际值不是 string
类型时,类型不匹配。
我们可以使用actual.Should().BeEquivalentTo(expected, options => options.WithAutoConversion())
,但这会使整个实际集合成为IDictionary<string, string>
的集合,这对于比较日期没有用处。
我组装了一个会显示相同问题的测试用例:
var expected = new List<Dictionary<string, string>>();
expected.Add(new Dictionary<string, string>
"Name", "Moon Inc.",
"Number", "42",
"Date", "2018-12-31"
);
var actual = new List<ExpandoObject>();
dynamic eo = new ExpandoObject();
eo.Name = "Moon Inc.";
eo.Number = 42;
eo.Date = new DateTime(2018, 12, 31);
actual.Add(eo);
actual.Should().BeEquivalentTo(expected, options => options);
/*
This throws:
NUnit.Framework.AssertionException:
Expected item[0][Number] to be System.String, but found System.Int32.
Expected item[0][Date] to be System.String, but found System.DateTime.
*/
actual.Should().BeEquivalentTo(expected, options => options.WithAutoConversion());
/*
This throws:
NUnit.Framework.AssertionException:
Expected item[0][Date] to be "2018-12-31" with a length of 10,
but "31-12-2018 0:00:00" has a length of 18.
/*
我尝试在使用方法中使接收类型动态化,例如:
actual.Should().BeEquivalentTo(expected, options => options
.Using<dynamic>(ctx => ctx.Subject.Should().Be(ctx.Expectation)).WhenTypeIs<DateTime>()
.Using<dynamic>(ctx => ctx.Subject.Should().Be(ctx.Expectation)).WhenTypeIs<int>());
/*
NUnit.Framework.AssertionException:
Expected item[0][Number] to be System.String, but found System.Int32.
Expected item[0][Date] to be System.String, but found System.DateTime.
*/
将双方解析为DateTime
并使用自动转换也不起作用,因为actual
类型不被视为DateTime
,而是string
:
actual.Should().BeEquivalentTo(expected, options => options
.Using<dynamic>(
ctx =>
DateTime.ParseExact(ctx.Subject, "yyy-MM-dd", CultureInfo.InvariantCulture)
.Should().Be(DateTime.ParseExact(ctx.Expectation, "yyy-MM-dd", CultureInfo.InvariantCulture)))
.WhenTypeIs<DateTime>()
.WithAutoConversion());
/*
NUnit.Framework.AssertionException:
Expected item[0][Date] to be "2018-12-31" with a length of 10,
but "31-12-2018 0:00:00" has a length of 18.
*/
FluentAssertions 有什么方法可以实现这一点吗?
【问题讨论】:
【参考方案1】:如果没有其他帮助,您可以像这样实现自定义IEquivalencyStep
:
class WeakDateEquivalencyStep : IEquivalencyStep
public bool CanHandle(IEquivalencyValidationContext context, IEquivalencyAssertionOptions config)
if (context.IsRoot)
return false;
// handles situations when subject is date
// but expectation is string
return context.Subject is DateTime && context.Expectation is string;
public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config)
DateTime exp;
// we know that expection is string here
if (!DateTime.TryParse((string) context.Expectation, CultureInfo.InvariantCulture, DateTimeStyles.None, out exp))
// do something, your spec is invalid
throw new Exception($"Value context.Expectation does not represent valid date time");
context.Subject.Should().Be(exp, context.Because, context.BecauseArgs);
return true;
然后
actual.Should().BeEquivalentTo(expected, options =>
options.Using(new WeakDateEquivalencyStep()).WithAutoConversion());
【讨论】:
这非常适合我的情况,有了这些知识,我也可以将它扩展到其他类型。非常感谢!以上是关于使用 FluentAssertions 比较包含不同类型的两个字典集合的主要内容,如果未能解决你的问题,请参考以下文章
FluentAssertions 对象图比较,包括 JSON 对象
.Net FluentAssertions .Contains 不能正确比较对象
FluentAssertions Should.Equal 在集合上,包含空值