如何在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对象是在运行时解析的,因为我还没有找到一种方法来动态调用它们,同时在编译时仍然保留结果类型信息(即关于通过实体框架调用的存储过程返回的结果的类型信息)。如果你已经想出了如何做到这一点,那么说些什么。(我想和你谈谈。

如何在c#单元测试中断言两个EF集合(来自过程结果)之间的等价性

查看公共属性值比较的答案。但是,它处理单个对象,因此您需要自己对集合进行循环。例如,按相同字段排序,然后依次访问相同索引的两个集合中的每个条目,并应用比较操作符。

注意,这个回答中的代码假设两个对象的类型相同。修改它以接受两种不同的类型是相当简单的。

有争议的是,您最好实现Equals方法(例如,通过查看标识属性值),允许在集合比较中使用它,然后有一个单独的测试,通过两种机制检索一个对象,并使用所提到的方法比较每个对象的公共属性中的值,只是为了确保所有值都被填充。

使用FluentAssertions(它支持所有主要的单元测试框架),你可以做到这一点

originalResultList.ShouldAllBeEquivalentTo(mappedResultList);

到断言两个集合在结构上是等价的