2009-04-07 11 views
14

Podejmuję pierwsze kroki z MsTest i Moq i chciałbym przetestować klasę repozytorium Linq2SQL. Problem polega na tym, że nie chcę, aby testy jednostkowe nieustannie modyfikowały moją bazę danych programowania.Jednostka testująca repozytorium LINQ2SQL

Jakie byłoby najlepsze podejście do tego scenariusza?

  • Niech każdy test działać na mojej bazy rozwoju rzeczywistym, ale upewnij się, że każdy test sprząta po sobie
  • utworzyć duplikat mojej bazy rozwojowej i dbml dla testów jednostkowych i użyć tego kontekstu, zamiast więc mogę usunąć cała baza danych przed każdym uruchomieniem testu
  • Znajdź jakiś wymyślny sposób kpienia z Datacontext (proszę pamiętać, że jestem totalnym Moq noob).
  • Coś zupełnie innego? Być może coś, co zautomatyzuje konfigurowanie bazy danych dla mnie przed każdym uruchomieniem testu?

Edit: Właśnie dowiedziałem się, że MbUnit posiada atrybut wycofywania że odwraca żadnych operacji na bazie danych prowadzonej przez przypadek testowy. Nie jestem szczególnie przywiązany do MSTest, więc czy to może być łatwa odpowiedź na mój problem?

Odpowiedz

1

grałem trochę z MbUnit i dowiedział się, że w większości przypadków testowych, można uciec bez szyderczy DataContext za pomocą [ROLLBACK] atrybut MbUnit za .

Niestety istnieją również przypadki, gdy atrybut wywołuje dziwne efekty uboczne, takie jak ładowanie obiektu linq z bazy danych, zmienianie jednej właściwości (bez submignanges), a następnie ponowne ładowanie tego samego obiektu. Zwykle nie powoduje to aktualizacji kwerendy w bazie danych, ale z metody testowej wygląda tak, jakby aktualizacja została natychmiast wykonana, gdy tylko zmienię właściwość encji linq.

Nie jest to idealne rozwiązanie, ale myślę, że pójdę z atrybutem [ROLLBACK], ponieważ jest to mniejszy wysiłek i działa wystarczająco dobrze dla mnie.

14

Poszedłem z kpiną/fałszowaniem bazy danych za pomocą niektórych klas wrapperów + fałszywą implementację opartą na http://andrewtokeley.net/archive/2008/07/06/mocking-linq-to-sql-datacontext.aspx. Zauważ, że skończyłem implementować logikę SubmitChanges w moim opakowaniu kontekstowym fake data, aby przetestować logikę walidacji w częściowej implementacji klasy mojej jednostki. Myślę, że to była naprawdę trudna część, która różniła się znacznie od implementacji Tokeleya.

będę to moje wykonanie FakeDataContextWrapper poniżej:

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

Yup, to co ja też zrobiłem. To i utworzyłem testy integracyjne, które przebiegły w bazie danych. Linq to Sql jest całkiem niezłe pod tym względem - możesz nukeować i tworzyć bazę danych bezpośrednio z kontekstu danych. – Will

2

Miałem podobną potrzebę - aby przetestować jednostki klasy Linq do Sql, więc zrobiłem mały zestaw klas, aby uzyskać fałszywe datacontext, ITables i IQueryables do kwerend.

Umieściłem kod w poście na blogu "Mock and Stub for Linq to Sql". Używa Moq i może zapewnić wystarczającą funkcjonalność dla testów, których szukasz, nie trafiając do bazy danych.