Pregunta

¿Alguien tiene un buen enfoque hacia la unidad que prueba sus UserTypes?

A modo de ejemplo, tengo un objeto en mi modelo llamado DateRange, que tiene un inicio y un fin de DatePoint. Además de hacer que las operaciones de tipo de rango estén disponibles para dos DateTimes, estos objetos me permiten ajustar la precisión para la tarea en cuestión (es decir, Día, Hora, Minuto, etc.). Cuando se almacena en la base de datos para una aplicación en la que estoy trabajando, solo necesito almacenar el inicio y el final como DateTime, no se permiten valores nulos. No puedo pensar en cómo mapear esto sin un UserType, así que tengo:

/// <summary>User type to deal with <see cref="DateRange"/> persistence for time sheet tracking.</summary>
public class TimePeriodType : IUserType
{

    public SqlType[] SqlTypes {
        get {
            var types = new SqlType[2];
            types[0] = new SqlType(DbType.DateTime);
            types[1] = new SqlType(DbType.DateTime);
            return types;  

        }
    }

    public Type ReturnedType
    {
        get { return typeof(DateRange); }
    }

    /// <summary>Just return <see cref="DateRange.Equals(object)"/></summary>
    public new bool Equals(object x, object y)
    {
        return x != null && x.Equals(y);
    }

    /// <summary>Just return <see cref="DateRange.GetHashCode"/></summary>
    public int GetHashCode(object x)
    {
        return x.GetHashCode();
    }

    public object NullSafeGet(IDataReader rs, string[] names, object owner)
    {
        var start = (DateTime)NHibernateUtil.DateTime.NullSafeGet(rs, names[0]);
        var end = (DateTime)NHibernateUtil.DateTime.NullSafeGet(rs, names[1]);

        return new DateRange(start, end, TimeSlice.Minute);
    }

    public void NullSafeSet(IDbCommand cmd, object value, int index) {
        Check.RequireNotNull<DateRange>(value);
        Check.RequireArgType<DateRange>(value);
        var dateRange = ((DateRange)value);

        NHibernateUtil.DateTime.NullSafeSet(cmd, dateRange.Start, index);
        NHibernateUtil.DateTime.NullSafeSet(cmd, dateRange.End, index);
    }

    public object DeepCopy(object value) {
        Check.RequireNotNull<DateRange>(value);
        Check.RequireArgType<DateRange>(value);
        var dateRange = ((DateRange) value);

        return new DateRange(dateRange.Start, dateRange.End);
    }

    public bool IsMutable
    {
        get { return false; }
    }

    public object Replace(object original, object target, object owner) {
        //because it is immutable so we can just return it as is  
        return original;
    }

    public object Assemble(object cached, object owner) {
        //Used for caching, as it is immutable we can just return it as is  
        return cached;
    }

    public object Disassemble(object value) {
        //Used for caching, as it is immutable we can just return it as is  
        return value;
    }
}

}

Ahora estoy buscando una manera de demostrar que funciona. Gracias de antemano!

Saludos, Berryl

¿Fue útil?

Solución

Creé un tipo de usuario para System.Drawing.Color, y así es como lo probé con MSTest y Moq .

ColorUserType.cs:

public class ColorUserType : IUserType
{
    public object Assemble( object cached, object owner )
    {
        return cached;
    }

    public object DeepCopy( object value )
    {
        return value;
    }

    public object Disassemble( object value )
    {
        return value;
    }

    public new bool Equals( object x, object y )
    {
        if(ReferenceEquals(x, y ) )
        {
            return true;
        }
        if( x == null || y == null )
        {
            return false;
        }
        return x.Equals( y );
    }

    public int GetHashCode( object x )
    {
        return x == null ? typeof( Color ).GetHashCode() + 473 : x.GetHashCode();
    }

    public bool IsMutable
    {
        get 
        {
            return true;
        }
    }

    public object NullSafeGet( IDataReader rs, string[] names, object owner )
    {
        var obj = NHibernateUtil.String.NullSafeGet( rs, names[0] );
        if( obj == null )
        {
            return null;
        }
        return ColorTranslator.FromHtml( (string)obj );
    }

    public void NullSafeSet( IDbCommand cmd, object value, int index )
    {
        if( value == null )
        {
            ( (IDataParameter)cmd.Parameters[index] ).Value = DBNull.Value;
        }
        else
        {
            ( (IDataParameter)cmd.Parameters[index] ).Value = ColorTranslator.ToHtml( (Color)value );
        }
    }

    public object Replace( object original, object target, object owner )
    {
        return original;
    }

    public Type ReturnedType
    {
        get
        {
            return typeof( Color );
        }
    }

    public SqlType[] SqlTypes
    {
        get
        {
            return new[] { new SqlType( DbType.StringFixedLength ) };
        }
    }
}

ColorUserTypeTests.cs

    [TestClass]
    public class ColorUserTypeTests
    {
        public TestContext TestContext { get; set; }

        [TestMethod]
        public void AssembleTest()
        {
            var color = Color.Azure;
            var userType = new ColorUserType();
            var val = userType.Assemble( color, null );
            Assert.AreEqual( color, val );
        }

        [TestMethod]
        public void DeepCopyTest()
        {
            var color = Color.Azure;
            var userType = new ColorUserType();
            var val = userType.DeepCopy( color );
            Assert.AreEqual( color, val );
        }

        [TestMethod]
        public void DissasembleTest()
        {
            var color = Color.Azure;
            var userType = new ColorUserType();
            var val = userType.Disassemble( color );
            Assert.AreEqual( color, val );
        }

        [TestMethod]
        public void EqualsTest()
        {
            var color1 = Color.Azure;
            var color2 = Color.Bisque;
            var color3 = Color.Azure;
            var userType = new ColorUserType();

            var obj1 = (object)color1;
            var obj2 = obj1;

            Assert.IsFalse( userType.Equals( color1, color2 ) );
            Assert.IsTrue( userType.Equals( color1, color1 ) );
            Assert.IsTrue( userType.Equals( color1, color3 ) );
            Assert.IsFalse( userType.Equals( color1, null ) );
            Assert.IsFalse( userType.Equals( null, color1 ) );
            Assert.IsTrue( userType.Equals( null, null ) );
            Assert.IsTrue( userType.Equals( obj1, obj2 ) );
        }

        [TestMethod]
        public void GetHashCodeTest()
        {
            var color = Color.Azure;
            var userType = new ColorUserType();

            Assert.AreEqual( color.GetHashCode(), userType.GetHashCode( color ) );
            Assert.AreEqual( typeof( Color ).GetHashCode() + 473, userType.GetHashCode( null ) );
        }

        [TestMethod]
        public void IsMutableTest()
        {
            var userType = new ColorUserType();
            Assert.IsTrue( userType.IsMutable );
        }

        [TestMethod]
        public void NullSafeGetTest()
        {
            var dataReaderMock = new Mock();
            dataReaderMock.Setup( m => m.GetOrdinal( "white" ) ).Returns( 0 );
            dataReaderMock.Setup( m => m.IsDBNull( 0 ) ).Returns( false );
            dataReaderMock.Setup( m => m[0] ).Returns( "#ffffff" );

            var userType = new ColorUserType();
            var val = (Color)userType.NullSafeGet( dataReaderMock.Object, new[] { "white" }, null );

            Assert.AreEqual( "ffffffff", val.Name, "The wrong color was returned." );

            dataReaderMock.Setup( m => m.IsDBNull( It.IsAny() ) ).Returns( true );
            Assert.IsNull( userType.NullSafeGet( dataReaderMock.Object, new[] { "black" }, null ), "The color was not null." );

            dataReaderMock.VerifyAll();
        }

        [TestMethod]
        public void NullSafeSetTest()
        {
            const string color = "#ffffff";
            const int index = 0;

            var mockFactory = new MockFactory( MockBehavior.Default );

            var parameterMock = mockFactory.Create();
            parameterMock.SetupProperty( p => p.Value, string.Empty );

            var parameterCollectionMock = mockFactory.Create();
            parameterCollectionMock.Setup( m => m[0] ).Returns( parameterMock.Object );

            var commandMock = mockFactory.Create();
            commandMock.Setup( m => m.Parameters ).Returns( parameterCollectionMock.Object );

            var userType = new ColorUserType();

            userType.NullSafeSet( commandMock.Object, ColorTranslator.FromHtml( color ), index );
            Assert.AreEqual( 0, string.Compare( (string)( (IDataParameter)commandMock.Object.Parameters[0] ).Value, color, true ) );

            userType.NullSafeSet( commandMock.Object, null, index );
            Assert.AreEqual( DBNull.Value, ( (IDataParameter)commandMock.Object.Parameters[0] ).Value );

            mockFactory.VerifyAll();
        }

        [TestMethod]
        public void ReplaceTest()
        {
            var color = Color.Azure;
            var userType = new ColorUserType();
            Assert.AreEqual( color, userType.Replace( color, null, null ) );
        }

        [TestMethod]
        public void ReturnedTypeTest()
        {
            var userType = new ColorUserType();
            Assert.AreEqual( typeof( Color ), userType.ReturnedType );
        }

        [TestMethod]
        public void SqlTypesTest()
        {
            var userType = new ColorUserType();
            Assert.AreEqual( 1, userType.SqlTypes.Length );
            Assert.AreEqual( new SqlType( DbType.StringFixedLength ), userType.SqlTypes[0] );
        }
    }

Otros consejos

Estaba pensando que podría burlarme / falsificar algunas de las dependencias aquí, pero terminé decidiendo que la mejor manera de hacerlo era usando una base de datos.

Algunas cosas que aprendí en el camino:

1) vale la pena el esfuerzo al aprender las técnicas de NHibernate para tener un conjunto dedicado de herramientas, incluida una forma de configurar rápidamente un db y probar el dispositivo (el mismo tipo de herramientas ágiles que necesitará para todo lo demás, en realidad) y un laboratorio de pruebas dedicado en el que no tienes una inversión emocional.

2) los simulacros no se prestan a interfaces que no son de su propiedad, como IDataReader.

Saludos

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top