如何在c#单元测试中断言两个EF集合(来自过程结果)之间的等价性
本文关键字:过程 结果 之间 集合 两个 单元测试 中断 断言 EF | 更新日期: 2023-09-27 18:01:55
此时,我正在使用NUnit执行比较。(我愿意为这个测试使用不同的单元测试框架。)我使用ILGenerator来动态调用通过Entity Framework 6.0映射的存储过程。(存储过程只是从SQL Server 2012数据库中的表值用户定义函数中进行选择)
代码似乎工作正常,但我有麻烦让我的断言通过。下面是我的错误:
Expected: equivalent to <
<CT_EntityDataModel.PROC_CT_Select_udfFacilityHolds_Global_Result>,
<CT_EntityDataModel.PROC_CT_Select_udfFacilityHolds_Global_Result>,
<CT_EntityDataModel.PROC_CT_Select_udfFacilityHolds_Global_Result>,
<CT_EntityDataModel.PROC_CT_Select_udfFacilityHolds_Global_Result>,
<CT_EntityDataModel.PROC_CT_Select_udfFacilityHolds_Global_Result>,
<CT_EntityDataModel.PROC_CT_Select_udfFacilityHolds_Global_Result> >
But was: <
<CT_EntityDataModel.PROC_CT_Select_udfFacilityHolds_Global_Result>,
<CT_EntityDataModel.PROC_CT_Select_udfFacilityHolds_Global_Result>,
<CT_EntityDataModel.PROC_CT_Select_udfFacilityHolds_Global_Result>,
<CT_EntityDataModel.PROC_CT_Select_udfFacilityHolds_Global_Result>,
<CT_EntityDataModel.PROC_CT_Select_udfFacilityHolds_Global_Result>,
<CT_EntityDataModel.PROC_CT_Select_udfFacilityHolds_Global_Result> >
at NUnit.Framework.Assert.That(Object actual, IResolveConstraint expression, String message, Object[] args)
at NUnit.Framework.CollectionAssert.AreEquivalent(IEnumerable expected, IEnumerable actual, String message, Object[] args)
at NUnit.Framework.CollectionAssert.AreEquivalent(IEnumerable expected, IEnumerable actual)
at CT.Tests.WpfControlsTests.ControlGeneratingMediator_AreDataProviderResultsPopulatingCorrectly_ReturnsTrue() in WPFControlsTests.cs: line 1338
NUnit.Framework.CollectionAssert.AreEquivalent方法的摘要如下:
"断言预期值和实际值相等,包含相同的值对象,但匹配的顺序可以是任意的。"
由于集合中的对象来自不同的操作序列,我不确定是否要断言对象是相同的(或具有相同的哈希值);无论如何,我想断言对象具有匹配的属性值。我希望我不需要重写相等比较操作或执行一些疯狂的hack来完成此操作。
下面是负责任的测试:
using NUnit.Framework;
// . . .
[Test]
public void ControlGeneratingMediator_AreDataProviderResultsPopulatingCorrectly_ReturnsTrue()
{
var instance = SharedSetupSingleton.Instance;
var methodInfo = instance.GetMethodInfo();
SimpleLoadDynamicGridMediatorFactory factory = new SimpleLoadDynamicGridMediatorFactory();
var commandstrategymediator = factory.Build(SimpleMediatorFactory.MediatorType.LoadDynamicGridMediator_Simple, methodInfo);
var commandstrategymediatorConverted = (LoadDynamicGridMediator)commandstrategymediator;
commandstrategymediatorConverted.CallStrategy(methodInfo); // we really shouldn't need to pass in the methodInfo when it's already defined during construction.
var newMediator = factory.Build(SimpleMediatorFactory.MediatorType.ControlGeneratingMediator_Simple, methodInfo);
newMediator.CallStrategy();
//newMediator.InputCommandStrategyMediator = commandstrategymediatorConverted;
var originalResultList = new List<dynamic>();
// each item should be of type: {CT_EntityDataModel.PROC_CT_Select_udfFacilityHolds_Global_Result}
// I used the following foreach loops to work around an "InvalidOperationException: The result of a query cannot be enumerated more than once."
foreach (var item in commandstrategymediatorConverted.ProviderResultSet)
{
originalResultList.Add(item);
}
var mappedResultList = new List<dynamic>();
foreach (var item in newMediator.Strategy.Results)
{
mappedResultList.Add(item);
}
CollectionAssert.AreEquivalent(originalResultList, mappedResultList);
// Are both of these parameters pulling on the same IEnumerable?
//Assert.AreEqual(commandstrategymediatorConverted.ProviderResultSet, newMediator.Strategy.Results);
}
(注意:从最佳实践的角度来看,我意识到这个测试相当难看,看起来更像一个集成测试而不是一个实际的单元测试。如果您想批评测试,请提供对代码进行合理改进的代码示例。(请记住,EF对象是在运行时解析的,因为我还没有找到一种方法来动态调用它们,同时在编译时仍然保留结果类型信息(即关于通过实体框架调用的存储过程返回的结果的类型信息)。如果你已经想出了如何做到这一点,那么请说些什么。(我想和你谈谈。
查看公共属性值比较的答案。但是,它处理单个对象,因此您需要自己对集合进行循环。例如,按相同字段排序,然后依次访问相同索引的两个集合中的每个条目,并应用比较操作符。
注意,这个回答中的代码假设两个对象的类型相同。修改它以接受两种不同的类型是相当简单的。
有争议的是,您最好实现Equals
方法(例如,通过查看标识属性值),允许在集合比较中使用它,然后有一个单独的测试,通过两种机制检索一个对象,并使用所提到的方法比较每个对象的公共属性中的值,只是为了确保所有值都被填充。
使用FluentAssertions(它支持所有主要的单元测试框架),你可以做到这一点
originalResultList.ShouldAllBeEquivalentTo(mappedResultList);
到断言两个集合在结构上是等价的