【问题标题】:Strange failure in Nunit test with NHibernateNHibernate 在 Nunit 测试中的奇怪失败
【发布时间】: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)
    {
    }
}

【问题讨论】:

    标签: c# testing nhibernate nunit fluent-assertions


    【解决方案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 的版本更好。更简单和线性
    猜你喜欢
    • 2010-09-21
    • 2012-03-25
    • 2020-06-22
    • 1970-01-01
    • 2021-10-11
    • 2019-03-04
    • 2015-02-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多