2009-09-02 8 views
4

私はアクセスせずにデータベースにアクセスする場合をシミュレートする方法を定義しようとしています...それはおそらくかなり狂っているようですが、そうではありません。Mockingはメソッド内にラップされた機能を置き換えることはできますか?

public IDevice GetDeviceFromRepository(string name) 
    { 
     IDevice device = null; 
     IDbConnection connection = new SqlConnection(ConnectionString); 
     connection.Open(); 
     try 
     { 
      IDbCommand command = connection.CreateCommand(); 
      command.CommandText = string.Format("SELECT DEVICE_ID,DEVICE_NAME FROM DEVICE WHERE DEVICE_NAME='{0}'", name); 
      IDataReader dataReader = command.ExecuteReader(); 
      if(dataReader.NextResult()) 
      { 
       device = new Device(dataReader.GetInt32(0),dataReader.GetString(1)); 
      } 
     } 
     finally 
     { 
      connection.Close(); 
     } 
     return device; 
    } 

私が読まれているものを制御できるようにしたIDataReaderを模擬するふりをしています:ここで

は、私がテストしたい方法についての一例です。 (部品番号のフレームワークを使用して)そのような何か:

[TestMethod()] 
    public void GetDeviceFromRepositoryTest() 
    { 
     Mock<IDataReader> dataReaderMock = new Mock<IDataReader>(); 
     dataReaderMock.Setup(x => x.NextResult()).Returns(true); 
     dataReaderMock.Setup(x => x.GetInt32(0)).Returns(000); 
     dataReaderMock.Setup(x => x.GetString(1)).Returns("myName"); 
     Mock<IDbCommand> commandMock = new Mock<IDbCommand>(); 
     commandMock.Setup(x => x.ExecuteReader()).Returns(dataReaderMock.Object); 
     Mock<RemoveDeviceManager> removeMock = new Mock<RemoveDeviceManager>(); 
     removeMock.Setup() 
     RemoveDeviceManager target =new RemoveDeviceManager(new Device(000, "myName")); 
     string name = string.Empty; 
     IDevice expected = new Device(000, "myName"); // TODO: Initialize to an appropriate value 
     IDevice actual; 
     actual = target.GetDeviceFromRepository(name); 
     Assert.AreEqual(expected.SerialNumber, actual.SerialNumber); 
     Assert.AreEqual(expected.Name, actual.Name); 
    } 

私の質問は、私は嘲笑1でのIDataReaderを交換する方法GetDeviceFromRepositoryを強制することができるかどうかです。

+0

ここでの主な問題は、我々があることを行っているいくつかのレガシーコードを持っています新しいソリューションに適合します。 1つの要件は、単体テストと自動ビルドでカバレッジが95%以上となることです。私がしたいのは、屈折計をあまり実行せずにこのテストを迅速に実行し、日に複数回自動的にビルドするための外部リソース(データベース)にアクセスせずに、このカバレッジを達成するためのユニットテストを作成することです。これがこのアプローチに従う理由です。 –

答えて

5

現在、Moqを使用していますが、Typemock Isolator(免責事項 - 私はTypemockで働いていました)を使用しない限り、探している機能が依存性注入なしで達成できないと思います。

アイソレータは、以前に作成した偽のオブジェクトとオブジェクトの将来のインスタンスを交換可能に「将来のオブジェクト」と呼ばれる機能があります。

// Create fake (stub/mock whateever) objects 
var fakeSqlConnection = Isolate.Fake.Instance<SqlConnection>(); 
var fakeCommand = Isolate.Fake.Instance<SqlCommand>(); 
Isolate.WhenCalled(() => fakeSqlConnection.CreateCommand()).WillReturn(fakeCommand); 

var fakeReader = Isolate.Fake.Instance<SqlDataReader>(); 
Isolate.WhenCalled(() => fakeCommand.ExecuteReader()).WillReturn(fakeReader); 

// Next time SQLConnection is instantiated replace with our fake 
Isolate.Swap.NextInstance<SqlConnection>().With(fakeSqlConnection); 
5

ここでの問題は最終的には直接の依存関係であると私は思うでしょうSqlConnection。あなたのコードがIDbCommandへのアクセスを得るような依存性注入のいくつかの変種を使用する場合、どのように構築されるのかを知らずに、あなたは面倒なことなくモックを注入することができます。

これはあなたの質問にはあまり答えていないと私は理解していますが、長期的には説明したようなことをすることで、はるかに優れたテスト容易性が得られます。

5

Dependency Injectionへの移行がより良い長期的な解決策であるというフランクの答えに同意しますが、全体を噛み砕かずにその方向に進むための中間的なステップがあります。

ことの一つは、あなたのクラス内で保護された仮想メソッドにするIDbConnectionクラスの建設を移動することです:

protected virtual IDbConnection CreateConnection() 
{ 
    return new SqlConnection(ConnectionString); 
} 

次に、あなたがそうのようなあなたのクラスのテストバージョンを作成することができます

public class TestingRemoteDeviceManager : RemoteDeviceManager 
{ 
    public override IDbConnection CreateConnection() 
    { 
     IDbConnection conn = new Mock<IDbConnection>(); 
     //mock out the rest of the interface, as well as the IDbCommand and 
     //IDataReader interfaces 
     return conn; 
    } 
} 

具体的なSqlConnectionの代わりにMockまたは偽のIDbConnectionを返します。その偽のIDbCommandオブジェクトは偽のIDataReaderオブジェクトを返すことができます。

5

マントラはtest until fear is transformed in boredomです。私はあなたがここでその行を越えたと思う。あなたはデータリーダーの制御を取る場合は、あなたがテストしているだけのコードはこれです:良いです、ここでテストするためにほとんど何もありませんが、

device = new Device(dataReader.GetInt32(0),dataReader.GetString(1)); 

:データ層は、シンプルかつ愚かでなければなりません。だからあなたのデータ層を単体テストしようとしないでください。あなたがそれをテストしなければならないと感じたら、統合 - それを実際のデータベースに対してテストしてください。

もちろん、データ層をIDeviceRepositoryインターフェイスの背後に隠しておくと、簡単に模擬してのコードをテストすることができます。コードはまだ良いアイデアです。

関連する問題