2013-11-04 133 views
6

我是單元測試新手,希望得到一些幫助。我首先使用存儲庫模式使用代碼。我有一個包含所謂的通用信息庫通用CRUD操作的通用存儲庫(見一擊)使用Moq和庫模式進行單元測試

public abstract class GenericRepository<T> where T : class 
    { 
     private HolidayDatabaseContext _dataContext; 
     private readonly IDbSet<T> _dbset; 
     protected GenericRepository(IDbFactory databaseFactory) 
     { 
      DatabaseFactory = databaseFactory; 
      _dbset = DataContext.Set<T>(); 
     } 
     protected IDbFactory DatabaseFactory 
     { 
      get; 
      private set; 
     } 
     protected HolidayDatabaseContext DataContext 
     { 
      get { return _dataContext ?? (_dataContext = DatabaseFactory.Get()); } 
     } 

     public virtual void Add(T entity) 
     { 
      _dbset.Add(entity); 
     } 
     public virtual void Update(T entity) 
     { 
      _dataContext.Entry(entity).State = EntityState.Modified; 
     } 
     public virtual void Delete(T entity) 
     { 
      _dbset.Remove(entity); 
     } 
     public virtual IEnumerable<T> Enumerable() 
     { 
      return _dbset.AsEnumerable<T>(); 
     } 

     public virtual IQueryable<T> List() 
     { 
      return _dbset.AsQueryable<T>(); 
     } 

     public virtual T GetSingleById(int id) 
     { 
      return _dbset.Find(id); 
     } 

     public void Save() 
     { 
      _dataContext.SaveChanges(); 
     } 

    } 

然後我繼承了它進入用戶系統信息庫,並創造了一些具體的方法。見下文

public class UserRepository : GenericRepository<User>, IUserRepository 
    { 
     public UserRepository(IDbFactory databaseFactory) 
      : base(databaseFactory) { } 


     public int HolidayEntitlement(int userId) 
     { 
      return HolidayEntitlement(userId, DateTime.Now); 
     } 
     public int HolidayEntitlement(int userId, DateTime dateTime) 
     { 
      //Get the User 
      var user = this.GetSingleById(userId); 

      //Work Total Entitlement 
      int entitlement = user.BaseHolidayEntitlement; 

      //Years in Service 
      entitlement += (dateTime - user.EmploymentStartDate).Days/365; 

      return entitlement; 



     } 


     public int RemainingHolidayEntitlement(int userId) 
     { 
      return RemainingHolidayEntitlement(userId, DateTime.Now); 
     } 

     public int RemainingHolidayEntitlement(int userId, DateTime dateTime) 
     { 
      return int.MinValue; 
     } 
    } 

我想單元測試HolidayEntitlement(INT用戶id,日期時間日期時間),但我需要在

我寫了這個作爲測試的方法來嘲笑GetSingleById一部分,但它不編譯。

[TestMethod] 
     public void GetHolidayEntitlement25() 
     { 
      //How to write this Unit test 


      //Setup 
      var user = new User { AnnualHolidayIncrement = 1, BaseHolidayEntitlement = 25, EmploymentStartDate = new DateTime(2013, 1, 1),Id=1 }; 

      Mock<UserRepository> mock = new Mock<UserRepository>(); 
      mock.Setup(m => m.GetSingleById(1)).Returns(user); 

      Assert.AreEqual(25, mock.Object.HolidayEntitlement(1)); 
     } 

任何幫助,將不勝感激

回答

12

你好像在說,你只是想嘲弄接口的一部分。當你開始遇到這種情況時,這表明你正在混淆你的擔憂,並可能在某處做錯了事。

在這種情況下,您的存儲庫正在做很多不僅僅是CRUD,因此有多重責任(它應該只有一個,查找SOLID編程)。您正在存儲庫中執行業務邏輯,並且它不應該在那裏生存!除了簡單的CRUD操作以外的任何內容都應該移出到業務邏輯層。即您的HolidayEntitlement方法通過應用某些邏輯來計算某些內容,因此不是CRUD /存儲庫操作!

所以......你應該做的是將業務邏輯位移出一個新類,比如UserLogic。在UserLogic類中,您將使用注入的IUserRepository與您的存儲庫進行通信。在UserLogic這是你會把你的HolidayEntitlement方法,它會打電話給IUserRepository.GetSingleById。所以,當你接着測試你的UserLogic課程時,你會在你的模擬IUserRepository中注入那個存根版本爲GetSingleById,然後你將回到正確的軌道!

我希望這是有道理的/幫助?!

--ORIGINAL POST--

附:我的原帖說,你應該模擬接口,沒有實例,因此這仍然適用,我會離開這裏,以供參考:

你應該嘲笑IUserRepositoryUserRepository

這是因爲UserRepositoryIUserRepository的實現。你想說你正在給它一個新的實現,即你的模擬。目前您正在使用ACTUALUserRepository

+0

謝謝你的快速反應,我的意思說的是,我的通用信息庫處理CRUD操作。然後我爲每個從generic繼承的實體定製了Repositories。我想我在這裏犯的錯誤是將「假日權利」方法放入存儲庫中。它應該在邏輯層,謝謝你指出我正確的方向,我將分裂出來,並嘗試再次創建單元測試。 –

+1

@MikeRoss - 是的,很容易犯錯,但我很高興看到你編寫單元測試。它們可以幫助你儘早拿起這些東西。他們確保你把東西放在正確的地方,因爲如果你不能嘲笑,那麼你知道有什麼不對。保持良好的做法! – Belogix

+0

@丹尼爾 - 原來的問題是「我想單元測試HolidayEntitlement ...」,這並不意味着「我如何測試CRUD」。這可能是**你的**問題,在這種情況下,我會建議提出你自己的問題,並明確地詢問你想知道的內容。 – Belogix

4

嘲諷一般使用時,您需要提供一個假依賴,在這種情況下,你似乎是試圖嘲笑被測系統(SUT),它並沒有真正意義 - 有字面上是沒有意義的,因爲你的測試實際上並沒有告訴你任何關於UserRepository的行爲;所有你正在做的是測試,如果你正確設置你的模擬,這是不是很有用!

您提供的測試代碼似乎表明您要測試UserRepository.HolidayEntitlement

我會更傾向於將類似這樣的功能移出您的存儲庫類,並轉移到單獨的業務邏輯類型類。通過這種方式,您可以在完全隔離的情況下測試計算用戶假期權利的邏輯,這是單元測試的主要原則。

爲了測試這個功能做什麼它應該做的(即執行基於User的性質計算),你需要確保無論User實例正在該函數內手術是100%的分離和下你的控制 - 無論是Mock還是Fake(Stub)用戶實例,在這種情況下,Mocks是一個很好的選擇,因爲你只需要實現SUT需要的依賴項部分。

所以,你可以做的是這樣的:

定義一個接口,用於用戶

public interface IUser 
{ 
    int BaseHolidayEntitlement{get;set;} 
    DateTime EmploymentStartDate {get;set;} 
    //other properties for a User here 
} 

實現此對您的用戶類

public class User:IUser 
{ 
    //implemement your properties here 
    public int BaseHolidayEntitlement{get;set;} 
    public DateTime EmploymentStartDate {get;set;} 
    //and so on 
} 

創建一個類用於用戶邏輯

public class UserRules 
{ 
    public int GetHolidayEntitlement(IUser user,DateTime dateTime) 
    { 
    //perform your logic here and return the result 
    } 
} 

現在你的測試變得更簡單,甚至不需要庫

[TestMethod] 
public void GetHolidayEntitlement_WithBase25_Returns25() 
{ 
    //Arrange 
    var user = new Mock<IUser>(); 
    //setup known, controlled property values on the mock: 
    user.SetupGet(u=>u.BaseHolidayEntitlement).Returns(25); 
    user.SetupGet(u=>u.EmploymentStartDate).Returns(new DateTime(2013,1,1)); 
    var sut = new UserRules(); 
    int expected = 25; 
    //Act 
    int actual = sut.GetHolidayEntitlement(user.Object,DateTime.UtcNow); 
    //Assert 
    Assert.AreEqual(expected,actual,"GetHolidayEntitlement isn't working right..."); 
} 
+0

感謝您的迴應Stephen,我認爲我已經通過在那裏添加業務邏輯而陷入緊密耦合的設計。我猜想我的模式知識不適應。 –

+0

@MikeRoss這是單元測試的好處 - 讓你仔細看看你的設計並發現緊密耦合。與它好運! –

+0

只是一件小事 - 你已經用2個參數定義了GetHolidayEntitlement,並且在測試方法中你只傳遞了一個 – Sasha

相關問題