2009-04-07 10 views
14

Sto facendo i primi passi con MsTest e Moq e vorrei testare una classe di repository Linq2SQL. Il problema è che non voglio che i test di unità modifichino in modo permanente il mio database di sviluppo.Unità testata su un repository LINQ2SQL

Quale sarebbe l'approccio migliore per questo scenario?

  • Lasciate ogni test operare sul mio database di sviluppo vero e proprio, ma assicurarsi che ogni test pulisce dopo essersi
  • creare un duplicato del mio database di sviluppo e dbml per la prova di unità e utilizzare quel contesto, invece, così posso cancellare l'intero database prima di ogni test eseguito
  • Trova un modo elaborato per deridere il Datacontext (tieni presente che sono un Moq noob totale).
  • Qualcosa di completamente diverso? Forse qualcosa che automatizzerebbe la configurazione del database per me prima di ogni prova?

Edit: Ho appena saputo che MBUnit ha un attributo di rollback che inverte eventuali operazioni di database gestiti da un banco di prova. Non sono particolarmente legato a MSTest, quindi questa potrebbe essere una risposta facile al mio problema?

risposta

1

ho giocato un po 'con MBUnit e imparato che, per la maggior parte dei casi di test, è possibile ottenere via senza beffardo il DataContext utilizzando l'attributo di MBUnit [ROLLBACK] .

Sfortunatamente ci sono anche casi in cui l'attributo produce strani effetti collaterali, come il caricamento di un'entità linq dal database, la modifica di una proprietà (senza submitchanges), quindi il caricamento della stessa entità nuovamente. Di solito questo non comporta alcuna query di aggiornamento sul database, ma dal metodo di test appare come se l'aggiornamento fosse eseguito immediatamente non appena avessi cambiato la proprietà dell'entità linq.

Non è una soluzione perfetta, ma penso che andrò con l'attributo [ROLLBACK] poiché è meno impegnativo e funziona abbastanza bene per me.

14

Sono andato con il mocking/falso del database utilizzando alcune classi wrapper + un'implementazione falsa basata su http://andrewtokeley.net/archive/2008/07/06/mocking-linq-to-sql-datacontext.aspx. Si noti che ho finito per implementare la logica di SubmitChanges nel mio involucro di contesto di dati falsi per testare la logica di convalida nell'implementazione della classe parziale della mia entità. Penso che questa sia stata l'unica parte difficile che differiva sostanzialmente dall'implementazione di Tokeley.

io includo la mia implementazione FakeDataContextWrapper di seguito:

public class FakeDataContextWrapper : IDataContextWrapper 
{ 

    public DataContext Context 
    { 
     get { return null; } 
    } 

    private List<object> Added = new List<object>(); 
    private List<object> Deleted = new List<object>(); 

    private readonly IFakeDatabase mockDatabase; 

    public FakeDataContextWrapper(IFakeDatabase database) 
    { 
     mockDatabase = database; 
    } 

    protected List<T> InternalTable<T>() where T : class 
    { 
     return (List<T>)mockDatabase.Tables[typeof(T)]; 
    } 

    #region IDataContextWrapper Members 

    public virtual IQueryable<T> Table<T>() where T : class 
    { 
     return mockDatabase.GetTable<T>(); 
    } 

    public virtual ITable Table(Type type) 
    { 
     return new FakeTable(mockDatabase.Tables[type], type); 
    } 

    public virtual void DeleteAllOnSubmit<T>(IEnumerable<T> entities) where T : class 
    { 
     foreach (var entity in entities) 
     { 
      DeleteOnSubmit(entity); 
     } 
    } 

    public virtual void DeleteOnSubmit<T>(T entity) where T : class 
    { 
     this.Deleted.Add(entity); 
    } 

    public virtual void InsertAllOnSubmit<T>(IEnumerable<T> entities) where T : class 
    { 
     foreach (var entity in entities) 
     { 
      InsertOnSubmit(entity); 
     } 
    } 

    public virtual void InsertOnSubmit<T>(T entity) where T : class 
    { 
     this.Added.Add(entity); 
    } 

    public virtual void SubmitChanges() 
    { 
     this.SubmitChanges(ConflictMode.FailOnFirstConflict); 
    } 

    public virtual void SubmitChanges(ConflictMode failureMode) 
    { 
     try 
     { 
      foreach (object obj in this.Added) 
      { 
       MethodInfo validator = obj.GetType().GetMethod("OnValidate", BindingFlags.Instance | BindingFlags.NonPublic); 
       if (validator != null) 
       { 

        validator.Invoke(obj, new object[] { ChangeAction.Insert }); 
       } 
       this.mockDatabase.Tables[obj.GetType()].Add(obj); 
      } 

      this.Added.Clear(); 

      foreach (object obj in this.Deleted) 
      { 
       MethodInfo validator = obj.GetType().GetMethod("OnValidate", BindingFlags.Instance | BindingFlags.NonPublic); 
       if (validator != null) 
       { 
        validator.Invoke(obj, new object[] { ChangeAction.Delete }); 
       } 
       this.mockDatabase.Tables[obj.GetType()].Remove(obj); 
      } 

      this.Deleted.Clear(); 

      foreach (KeyValuePair<Type, IList> tablePair in this.mockDatabase.Tables) 
      { 
       MethodInfo validator = tablePair.Key.GetMethod("OnValidate", BindingFlags.Instance | BindingFlags.NonPublic); 
       if (validator != null) 
       { 
        foreach (object obj in tablePair.Value) 
        { 
         validator.Invoke(obj, new object[] { ChangeAction.Update }); 
        } 
       } 
      } 
     } 
     catch (TargetInvocationException e) 
     { 
      throw e.InnerException; 
     } 
    } 

    public void Dispose() { } 

    #endregion 
} 
+1

Sì, è quello che ho fatto anche io. Questo, e ha creato test di integrazione che hanno funzionato contro un database. Linq to Sql è piuttosto buono sotto questo aspetto: puoi nuke e creare il database direttamente dal contesto dei dati. – Will

2

Avevo una simile necessità: testare le classi da Linq a Sql, quindi ho creato un piccolo insieme di classi per ottenere mock data, ITables e IQueryables nelle query.

Ho inserito il codice in un post sul blog "Mock and Stub for Linq to Sql". Utilizza Moq e potrebbe fornire funzionalità sufficienti per i test che si stanno tentando senza colpire il database.

Problemi correlati