กลไกการทำงานของ .NET method โดยส่วนใหญ่ จะมีกลไกการทำงานที่ต้องขึ้นกับเงื่อนไข ปัจจัยต่างๆจากภายนอกอย่างเช่น method ที่มีเรียกใช้ database หรือ web service ทำให้การทดสอบจำเป็นต้องแยก component ที่ต้องการทดสอบออกมาจากปัจจัยแวดล้อมต่างรอบๆ component ซึ่งสามารถทำได้โดยใช้ shim ที่อยู่ภายใต้การควบคุมในกระบวนการทดสอบ สามารถที่จะควบคุมผลลัพธ์ที่ได้การทำงานตามที่กำหนดในทุกๆครั้งที่เรียกใช้งาน ซึ่งทำให้การเขียน unit testing ทำได้ง่ายขึ้นมาก
การพัฒนา unit test สำหรับ method ที่ใช้ Entity Framework เพื่อเข้าถึงฐานข้อมูล ก็สามารถใช้ shim type ในการกำหนดชุดของข้อมูลเพื่อทำการทดสอบ ซึ่งการ query จะกระทำกับ property ของ DbContext ซึ่ง return IDbSet<T>
public partial class Entities : DbContext
{
public Entities(): base("name=Entities")
{
}
public IDbSet<CONFIG> CONFIG { get; set; }
...
}
ในการพัฒนา unit test จะต้องสร้าง shim type สำหรับ class “Entities” และแทนที่ property ที่ดึงข้อมูลจากฐานข้อมูลจริงแยกออกจากการทดสอบ ด้วยข้อมูลสำหรับทดสอบ จากตัวอย่างข้างบนคือ property “CONFIG” ซึ่งเป็นประเภท IDbSet<CONFIG> โดยจะทำการ returm DbSet<CONFIG> ที่เตรียมข้อมูลไว้สำหรับการทดสอบ
using (ShimsContext.Create())
{
ShimEntities.AllInstances.CONFIGGet =
(e) => {
return ... DbSet<CONFIG>
};
}
แต่ใน DbSet ไม่มี public constructor ทำให้ไม่สามารถสร้าง instance ของ DbSet ได้ จึงจำเป็นต้องสร้าง class ใหม่ที่ implement interface IDbSet<T> แทนการใช้ DbSet
public class TestDbSet<T> : IDbSet<T>, IQueryable, IEnumerable<T>
where T : class
{
ObservableCollection<T> _data;
IQueryable _query;
public TestDbSet() //: base()
{
_data = new ObservableCollection<T>();
_query = _data.AsQueryable();
}
public virtual T Find(params object[] keyValues)
{
throw new NotImplementedException("Derive from TestDbSet<T> and override Find");
}
public T Add(T item)
{
_data.Add(item);
return item;
}
public T Remove(T item)
{
_data.Remove(item);
return item;
}
public T Attach(T item)
{
_data.Add(item);
return item;
}
public T Create()
{
return Activator.CreateInstance<T>();
}
public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T
{
return Activator.CreateInstance<TDerivedEntity>();
}
public ObservableCollection<T> Local
{
get { return new ObservableCollection<T>(_data); }
}
Type IQueryable.ElementType
{
get { return _query.ElementType; }
}
System.Linq.Expressions.Expression IQueryable.Expression
{
get { return _query.Expression; }
}
IQueryProvider IQueryable.Provider
{
get { return _query.Provider; }
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return _data.GetEnumerator();
}
}
หลังจากนั้นสร้าง class อีกหนึ่ง class ที่ inherit มาจาก TestDbSet<T> ระบุ generic type เป็น model class ที่ต้องการในที่นี้คือ CONFIG และเขียน code override Find() method ของTestDbSet<T> เพื่อทำหน้าที่ค้นหา object ตาม key ที่ส่งมา ( สาเหตุ ต้องสร้าง class ที่ inherit มาจาก TestDbSet<T> เนื่องจาก แต่ละ model class อาจจะมี key ที่ไม่ตรงกัน ดังนั้นใน TestDbSet<T>.Find() method จึงไม่สามารถ implement code ที่ต้องการได้ )
public class ConfigDbSet : TestDbSet<Models.CONFIG>
{
public override Models.CONFIG Find(params object[] keyValues)
{
return this.SingleOrDefault(s => s.ID == (decimal)keyValues.Single());
}
}
จากนั้นกลับมาที่ unit test method ก็จะทำการ new ConfigDbSet() แล้วส่งข้อมูลที่สำหรับทดสอบกลับไปได้ โดยไม่ต้องใช้ข้อมูลจริงจากฐานข้อมูล
