14


6

私はMsTestとMoqで私の最初のステップを踏んでいます、そしてLinq2SQLリポジトリクラスをユニットテストしたいです。 問題は、単体テストで開発データベースを永久に変更したくないということです。

このシナリオに最適なアプローチはどれですか。

  • 各テストが私の実際の開発データベースで動作するようにしなさい、しかし各テストがそれ自身の後できれいになることを確かめなさい

  • 単体テスト用に開発用データベースとdbmlの複製を作成し、代わりにそのコンテキストを使用するので、各テストを実行する前にデータベース全体をクリアできます。

  • Datacontextを嘲笑するためのいくつかの巧妙な方法を見つけてください(私は完全にMoqのノブです)。

  • まったく違う何か? テストを実行する前に、データベースを自動的に設定するようなことがあるでしょうか。

*編集:*私はちょうどMBUnitがテストケースによって実行されるデータベース操作を元に戻すロールバック属性を持っていることを学びました。 私は特にMSTestに執着していないので、これは私の問題に対する簡単な答えになるでしょうか。

3 Answer


14


私はhttp://andrewtokeley.net/archive/2008/07/06/mocking-linq-to-sql-datacontext.aspxに基づく偽の実装であるラッパークラスを使用してデータベースを偽造/偽造しました。 エンティティの部分クラス実装で検証ロジックをテストするために、偽のデータコンテキストラッパーにSubmitChangesロジックを実装しました。 これが、Tokeleyの実装とは大きく異なる、唯一のトリッキーな部分だと思います。

以下にFakeDataContextWrapperの実装を含めます。

パブリッククラスFakeDataContextWrapper:IDataContextWrapper {

public DataContextコンテキスト{get {return null;} }}

追加されたプライベートリスト= new List();削除されたプライベートリスト= new List();

プライベート読み取り専用IFakeDatabase mockDatabase。

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

protected List InternalTable()ここでT:class {return(List)mockDatabase.Tables [typeof(T)]; }

#region IDataContextWrapperメンバー

パブリック仮想IQueryable Table()ここでT:class {return mockDatabase.GetTable();} }

パブリック仮想ITableテーブル(タイプタイプ){新しいFakeTableを返す(mockDatabase.Tables [タイプ]、タイプ); }

DeleteAllOnSubmit(IEnumerable entities)ここで、Tは以下のとおりです。 }}

パブリック仮想無効DeleteOnSubmit(T entity)ここで、T:class {this.Deleted.Add(entity);} }

public virtual void InsertAllOnSubmit(IEnumerable Entity)ここで、T:class {foreach(エンティティ内のvarエンティティ){InsertOnSubmit(entity);} }}

public virtual void InsertOnSubmit(T entity)ここで、T:class {this.Added.Add(entity);} }

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

public virtual void SubmitChanges(ConflictMode failureMode){try {foreach(object obj this.Added)} {MethodInfo validator = obj.GetType()。GetMethod( "OnValidate"、BindingFlags.Instance | BindingFlags.NonPublic); if(バリデータ!= null){

validator.Invoke(obj、new object [] {ChangeAction.Insert}); this.mockDatabase.Tables [obj.GetType()]。(obj)を追加します。 }

this.Added.Clear();

foreach(this.Deletedのオブジェクトobj){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()]。削除(obj); }

this.Deleted.Clear();

foreach(this.mockDatabase.TablesのKeyValuePair tablePair){MethodInfo validator = tablePair.Key.GetMethod( "OnValidate"、BindingFlags.Instance | BindingFlags.NonPublic); if(validator!= null){foreach(tablePair.Value内のオブジェクトobj){validator.Invoke(obj、new object [] {ChangeAction.Update}); catch(TargetInvocationException e){throw e.InnerException;}}}}} }}

public void Dispose(){}

#endregion}


2


私は同様の必要性を持っていました - Linq to Sqlクラスを単体テストする必要があったので、模擬データコンテキスト、ITables、IQueryablesをクエリに入れるための少数のクラスを作りました。

私はブログの記事にコードを入れました。 。 これはMoqを使用しており、データベースにアクセスすることなく、その後のテストに十分な機能を提供する可能性があります。


1


私はMBUnitを少し試してみましたが、ほとんどのテストケースでは、MBUnitを使用することでデータコンテキストを嘲笑することなく逃げることができることを学びました

属性。

残念ながら、データベースからlinqエンティティをロードし、1つのプロパティを変更し(送信変更を行わずに)、次に同じエンティティを再度ロードするなど、属性が奇妙な副作用を引き起こす場合もあります。 通常、これはデータベースに対して更新クエリを実行しませんが、テストメソッド内からは、linqエンティティプロパティを変更するとすぐに更新が直ちに実行されるように見えます。

完璧な解決策ではありませんが、私は[ROLLBACK]属性を使うことにします。