如果您不想模擬實體類,另一種方法是使用反射來設置私有/受保護的ID。
是的,我知道這通常不被看好,經常被認爲是某個地方糟糕設計的標誌。但在這種情況下,在你的NHibernate實體上有一個受保護的ID是標準範例,所以它似乎是一個相當合理的解決方案。
我們可以嘗試至少實現它。在我的例子中,95%的實體都使用一個Guid作爲唯一標識符,只有少數人使用整數。因此,我們的實體類通常實現一個非常簡單的HasID
接口:
public interface IHasID<T>
{
T ID { get; }
}
在實際的實體類,我們可以實現它是這樣的:
public class User : IHasID<Guid>
{
Guid ID { get; protected set; }
}
這個ID被映射到NHibernate的作爲主鍵通常的方式。
向這在我們的單元測試的設置中,我們可以用這個接口來提供一個方便的擴展方法:
public static T WithID<T, K>(this T o, K id) where T : class, IHasID<K>
{
if (o == null) return o;
o.GetType().InvokeMember("ID", BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, o, new object[] { id });
return o;
}
我們不必有HasID接口要做到這一點,但它意味着我們可以跳過一些額外的代碼 - 例如,我們不需要檢查ID是否實際得到支持。
擴展方法也返回原來的對象,因此在使用中我通常只是IT連鎖到構造函數的末尾:
var testUser = new User("Test User").WithID(new Guid("DC1BA89C-9DB2-48ac-8CE2-E61360970DF7"));
或實際的,因爲對我的GUID不在乎ID居然什麼是的,我有另一種擴展方法:
public static T WithNewGuid<T>(this T o) where T : class, IHasID<Guid>
{
if (o == null) return o;
o.GetType().InvokeMember("ID", BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, o, new object[] { Guid.NewGuid() });
return o;
}
而且在用法:
var testUser = new User("Test User").WithNewGuid();
由於這個工程只是完美。我可能有2號的症狀。它只是模擬了很多事情。我只是試圖保留它,而不是我需要測試的東西。例如,我不嘲笑用戶的用戶名,因爲它沒有在方法中使用。我想嘗試自動混合moq,但我不知道如何真正使用,似乎沒有真正的教程,所以我不能確定這是否會幫助我。 – chobo2 2011-02-01 23:40:00