2013-11-04 16 views
6

Sono un nuovo test dell'unità e vorrei un aiuto. Sto usando il codice prima con pattern di repository. Ho un repository generico che contiene operazioni CRUD generico chiamato Repository Generico (vedi colpo)Test unitario con moq e modello di deposito

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(); 
     } 

    } 

ho poi ereditato in un'User Repository e ha creato alcuni metodi specifici. vedi sotto

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; 
     } 
    } 

Vorrei test unità HolidayEntitlement (int userId, DateTime DateTime), ma ho bisogno di prendere in giro la parte GetSingleById nel metodo

Ho scritto questo come un test, ma non è così compilare.

[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)); 
     } 

Qualsiasi aiuto sarebbe apprezzato

risposta

12

Sembra che tu stia dicendo che si desidera solo per deridere parte dell'interfaccia. Quando inizi a incontrare questo tipo di situazione, ti suggerisce di mescolare le tue preoccupazioni e probabilmente di fare qualcosa di sbagliato da qualche parte.

In questo caso il repository sta facendo MUCH più di un semplice CRUD e quindi ha più responsabilità (dovrebbe avere solo una programmazione SOLID lookup). Stai eseguendo la logica di business nel repository e non dovrebbe viverci! Qualsiasi cosa diversa dalle semplici operazioni CRUD dovrebbe essere spostata nel livello di business logic. Cioè il tuo metodo HolidayEntitlement calcola qualcosa applicando una logica e quindi NON è un'operazione CRUD/repository!

Quindi ... Quello che dovresti fare è spostare i bit della logica di business in una nuova classe, ad esempio UserLogic. All'interno della classe UserLogic verrà utilizzato uno IUserRepository iniettato per comunicare con il repository. In UserLogic è dove si inserisce il proprio metodo HolidayEntitlement e si farebbe una chiamata a IUserRepository.GetSingleById. Quindi, quando testerai la tua classe UserLogic inietta nel tuo simulato IUserRepository che avrebbe la versione stub di GetSingleById e poi tornerai sulla strada giusta!

Spero che abbia senso/aiuti ?!

--original CINA--

P.S. Il mio post originale ha dichiarato che si dovrebbe prendere in giro le interfacce, non le istanze per cui questa si applica ancora e mi lascerete qui per riferimento:

Si dovrebbe essere beffardo IUserRepositoryNONUserRepository.

Questo perché UserRepository è un'implementazione di IUserRepository. Vuoi dire che stai dando una NUOVA implementazione, cioè la tua finta. Al momento si sta utilizzando la ACTUAL classe UserRepository.

+0

Grazie per la rapida risposta, quello che volevo dire è che il mio repository generico gestisce le operazioni CRUD . e poi ho creato dei repository personalizzati per ciascuna delle mie entità che ereditano dal generico. Immagino che l'errore che ho fatto qui stia mettendo il metodo "Holiday Holiday" nel repository. Dovrebbe essere in uno strato logico, grazie per avermi indicato nella giusta direzione, l'ho diviso e ho provato a creare di nuovo il test dell'unità. –

+1

@MikeRoss - Sì, gli errori sono facili da fare, ma sono lieto di vederti scrivere test unitari. Ti aiutano a raccogliere queste cose presto. Si assicurano che stai mettendo le cose nel posto giusto, perché se non puoi prendere in giro allora sai che qualcosa non va. Continuate le buone pratiche! – Belogix

+0

@Daniel - La domanda originale era "Vorrei testare l'unità HolidayEntitlement ..." che non significa "Come faccio a testare CRUD". Potrebbe essere la ** TUA domanda, nel qual caso consiglierei di sollevare la tua domanda e chiedere esplicitamente cosa vorresti sapere. – Belogix

4

Mocking è generalmente usato quando è necessario fornire un falso dipendenze e in questo caso si sembrano essere cercando di prendere in giro il test In Sistema (SUT), che in realtà non ha senso - non c'è letteralmente alcun punto perché il vostro test non ti sta effettivamente dicendo nulla sul comportamento di UserRepository; tutto quello che stai facendo è provare se hai configurato correttamente il tuo Mock che non è molto utile!

Il codice di prova che hai fornito sembra indicare che vuoi testare UserRepository.HolidayEntitlement.

Sarei molto più incline a spostare funzioni di questo tipo dalla classe repository e in una classe di tipo business logic separata. In questo modo è possibile testare la logica di calcolo del diritto alle ferie di un utente in totale isolamento, che è uno dei principali principi del test unitario.

Al fine di verificare che questa funzione fa quello che dovrebbe fare (cioè eseguire un calcolo basato sulla proprietà di un User) è necessario assicurarsi che tutto ciò che User esempio che viene operato all'interno di tale funzione è al 100% isolato e sotto il tuo controllo - sia con un'istanza Mock o Fake (Stub) dell'Utente, in questo caso Mock sono una scelta eccellente perché hai solo bisogno di implementare le parti della dipendenza che il tuo SUT avrà bisogno.

Allora, che cosa si potrebbe fare è questo:

Definire un'interfaccia per utente

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

implementare questo sulla vostra classe User

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

creare una classe per logica utente

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

Ora il test diventa molto più semplice e non ha nemmeno bisogno repository

[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

Grazie per la tua risposta Stephen, penso di essere caduto in un design strettamente accoppiato aggiungendo la logica di business lì. Immagino che stia sfogando la mia conoscenza del modello. –

+0

@MikeRoss è la cosa bella dei test unitari: ti dà una buona occhiata al tuo design e ti mette a punto l'accoppiamento stretto. buona fortuna con esso! –

+0

Solo una piccola cosa: hai definito GetHolidayEntitlement con 2 parametri, e nel metodo di prova hai passato solo uno – Sasha