NHibernate UserTypesの単体テスト
-
20-08-2019 - |
質問
UserTypesの単体テストに向けたアプローチはありますか?
例として、DateRangeというモデルにオブジェクトがあり、DatePointの開始とDatePointの終了があります。範囲タイプの操作を2つのDateTimesで使用できるようにすることに加えて、これらのオブジェクトを使用すると、手元のタスク(日、時間、分など)の精度を調整できます。作業中のアプリケーションのdbに保存する場合、開始と終了をDateTimeとして保存するだけで、nullは許可されません。 UserTypeなしでこれをマッピングする方法は考えられないので、次のようにします。
/// <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;
}
}
}
今、私はそれが機能することを証明する方法を探しています。事前に感謝します!
乾杯、 ベリル
解決
System.Drawing.Colorのユーザータイプを作成しました。MSTestと 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] ); } }
他のヒント
ここで依存関係のいくつかをモック/偽造できると考えていましたが、これを行うための最良の方法はデータベースを使用することでした。
途中で学んだこと:
1)NHibernateのテクニックを学ぶときは、dbをすばやく構成し、そのためのフィクスチャをテストする方法など、専用のツールセットを用意する価値があります(他のすべてに必要な同じ種類のアジャイルツール、本当に)感情的な投資をしていない専用のテストラボ。
2)モックは、IDataReaderなど、所有していないインターフェイスには適していません。
乾杯
所属していません StackOverflow