NHibernate 在 Nunit 测试中的奇怪失败

Posted

技术标签:

【中文标题】NHibernate 在 Nunit 测试中的奇怪失败【英文标题】:Strange failure in Nunit test with NHibernate 【发布时间】:2014-10-01 18:48:39 【问题描述】:

使用 NUnit 和 FluentAssertion 运行一个简单的测试我有这个失败的消息:

Expected object to be 

Gedi.Domain.Object.Entity.Persona

   Annullato = False
   Descrizione = "Persona1"
   Id = 1
, but found 

Gedi.Domain.Object.Entity.Persona

   Annullato = False
   Descrizione = "Persona1"
   Id = 1
.

但我没有看到差异。哪个可能是失败的原因?

这是测试方法

public void CanSaveAndLoadDocumento()
    
        //Arrange
        Documento documentoTarget = new Documento();
        Documento documentoActual;

        documentoTarget.Id = fixture.Create<int>();                 

        // Act
        using (IUnitOfWork uow = new UnitOfWork())
        
            uow.Start();
            documentoTarget.Persona = uow.ServiceRepositoryFor<Persona>().GetById(1);
            uow.DocumentoRepository.Create(documentoTarget);
            uow.Commit();
            uow.CloseConnection();

            uow.Start();
            documentoActual = uow.DocumentoRepository.GetById(documentoTarget.Id);          
            uow.CloseConnection();
        

        //Assert
        documentoActual.Persona.Should().Be(documentoTarget.Persona);

    

ID = 1 的角色是我直接在数据库中手写的

这是我与 NHibernate 一起使用的基础存储库

public abstract class RepositoryBase<TEntity, TKey> : IDisposable
    where TEntity : class, IKeyedEntity<TKey>
    where TKey : struct

    protected ISession _session;
    public RepositoryBase(ISession session)
    
        _session = session;         
           
    public void Create(TEntity entity)
    
        _session.SaveOrUpdate(entity);
           
    public TEntity GetById(TKey id)
    
        return _session.Get<TEntity>(id);
    


public class DocumentoRepository : RepositoryBase<Documento, int>

    public DocumentoRepository(ISession session)
        : base(session)
    
    

【问题讨论】:

【参考方案1】:

原因很简单 - DocumentoRepository.GetById(documentoTarget.Id) 创建 Persona 实体的新实例,而不是返回缓存的实例。流利的断言通过引用比较两个实体,并且您有断言失败。

您可以为您的Persona 类实现EqualsGetHashCode。或者使用ShouldBeEquivlentTo 断言对象图等价:

documentoActual.Persona.ShouldBeEquivlentTo(documentoTarget.Persona);

【讨论】:

如何为 .Should().Contain() 做类似的事情? 如何为 .Should().Contain() 做类似的事情?我对一组项目进行了其他测试,我发现的唯一方法是实现 ContainEquivalentOf find ShouldAllBeEquivalentTo(),不太喜欢它,因为我必须比较集合但看不到替代品【参考方案2】:

更好的版本: 这样,如果所有属性都相等,则两个对象相等

public abstract class EquatableObject<TObject>: IEquatable<TObject>
    where TObject : class   

    protected EquatableObject()
               
    

    public override int GetHashCode()
    
        IEnumerable<FieldInfo> fields = GetFields();
        int startValue = 17;
        int multiplier = 59;
        int hashCode = startValue;

        foreach (FieldInfo field in fields)
        
            object value = field.GetValue(this);
            if (value != null)
                hashCode = hashCode * multiplier + value.GetHashCode();
        
        return hashCode;
    

    public override bool Equals(object obj)
    
        if (obj == null)
            return false;

        TObject other = obj as TObject;

        return Equals(other);

    

    public virtual bool Equals(TObject other)
    
        if (other == null)
            return false;

        Type t = GetType();
        Type otherType = other.GetType();

        if (t != otherType)
            return false;

        FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
        foreach (FieldInfo field in fields)
        
            object value1 = field.GetValue(other);
            object value2 = field.GetValue(this);

            if (value1 == null)
            
                if (value2 != null)
                    return false;
            
            else if (!value1.Equals(value2))
                return false;
        
        return true;

    

    private IEnumerable<FieldInfo> GetFields()
    
        Type t = GetType();
        List<FieldInfo> fields = new List<FieldInfo>();
        while (t != typeof(object))
        
            fields.AddRange(t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public));

            t = t.BaseType;
        
        return fields;
    

解决添加

public override bool Equals(object obj)
    
        if (obj == null)
            return false;

        T other = obj as T;

        return Equals(other);

    

到我的班级

【讨论】:

请注意,这可能会改变 NHibernate 在其会话中处理您的对象的方式。如果你想使用值对象,你应该只实现相等成员。正如 Sergey Berezovskiy 建议的那样,只需使用 ShouldBeEquivalentTo,而不是这样做。 已更新,但是我同意带有 ShouldBeEquivlentTo 的版本更好。更简单和线性

以上是关于NHibernate 在 Nunit 测试中的奇怪失败的主要内容,如果未能解决你的问题,请参考以下文章

问题在Jenkins上运行NUnit测试

NUnit中的数据驱动测试?

Xamarin 中的 NUnit 异步测试不起作用

NUnit 5 Spring MVC 测试 NoSuchBeanDefinitionException 用于子模块中的 Autowired 依赖项

使用 Fluentassertions 和 Nunit 在单元测试中进行计数验证

如何在测试拆解时使用 NUnit 3 读取 ITestResult