2017-01-23 14 views
4

私はユニットテストを書くために必要ないくつかのレガシーコードで作業しています。以下の署名を持つデータアクセスメソッドがあります。実装はクラスであり単体テスト - いくつかの難しさを嘲笑

Task ExecuteReaderAsync(string procedureName, Parameters procedureParameters, 
    params Action<System.Data.IDataReader>[] actions); 

私はGetCustomObjectによって返された値を制御することができるさに苦しんだ何この

に似
private async Task<CustomObject> GetCustomObject(int id) 
{ 
    CustomObject obj = null; 
    await db.ExecuteReaderAsync("nameOfProcedure", some parameters, 
    dr => 
    { 
     obj = new CustomObject() 
     { 
      Prop1 = dr["Col1"], 
      Prop2 = dr["Col2"] 
     } 
    } 
    return obj; 
} 

テストしています。 ExecuteReaderAsyncが実際に返された場合、私はこのような設定をすることができました。

mockDataAccess.Setup(x => x.ExecuteReaderAsync("nameOfProcedure", It.IsAny<Parameters>())) 
    .Returns(Task.FromResult(new CustomeObject() { prop1 = "abc", prop2 = "def"};)); 

しかし、値を指定するロジックは、私が制御しないAction<IDataReader>です。 つまり、GetCustomObjectによって返されたオブジェクトの値を制御するために私が欲しいことをするために使うことができるトリックがあるかどうかは疑問です。

答えて

2

あなたはAction<IDataReader>のコントロールを持っていないので、この場合には行うことができ、ほとんどのアクションが失敗しないことを確認することです次の例

[TestClass] 
public class LegacyCodeTest { 
    [TestMethod] 
    public async Task TestExecuteReaderAsync() { 
     //Arrange 
     var mapping = new Dictionary<string, string> { 
      { "Col1", "abc" }, 
      { "Col2", "def" } 
     }; 

     var mockDataReader = new Mock<IDataReader>(); 
     mockDataReader 
      .Setup(m => m[It.IsAny<string>()]) 
      .Returns<string>(col => mapping[col]) 
      .Verifiable(); 

     var mockDataAccess = new Mock<IDataAccess>(); 
     mockDataAccess 
      .Setup(m => m.ExecuteReaderAsync("nameOfProcedure", It.IsAny<Parameters>(), It.IsAny<Action<System.Data.IDataReader>[]>())) 
      .Returns(Task.FromResult<object>(null)) 
      .Callback((string s, Parameters p, Action<System.Data.IDataReader>[] a) => { 

       if (a != null && a.Length > 0) { 
        a.ToList().ForEach(callback => callback(mockDataReader.Object)); 
       } 

      }) 
      .Verifiable(); 


     var sut = new SUT(mockDataAccess.Object); 

     //Act 
     var actual = await sut.MUT(2); 

     //Assert 
     mockDataAccess.Verify(); 
     mockDataReader.Verify(m => m["Col1"]); 
     mockDataReader.Verify(m => m["Col2"]); 

     actual.Should() 
      .NotBeNull() 
      .And 
      .Match<CustomObject>(c => c.Prop1 == mapping["Col1"] && c.Prop2 == mapping["Col2"]); 

    } 

    public interface IDataAccess { 
     Task ExecuteReaderAsync(string procedureName, Parameters procedureParameters, params Action<System.Data.IDataReader>[] actions); 
    } 

    public class Parameters { } 

    public class CustomObject { 
     public object Prop1 { get; set; } 
     public object Prop2 { get; set; } 
    } 

    public class SUT { 
     IDataAccess db; 

     public SUT(IDataAccess dataAccess) { 
      this.db = dataAccess; 
     } 

     public async Task<CustomObject> MUT(int id) { 
      var result = await GetCustomObject(id); 
      return result; 
     } 

     private async Task<CustomObject> GetCustomObject(int id) { 
      CustomObject obj = null; 
      await db.ExecuteReaderAsync("nameOfProcedure", null, 
      dr => { 
       obj = new CustomObject() { 
        Prop1 = dr["Col1"], 
        Prop2 = dr["Col2"] 
       }; 
      }); 
      return obj; 
     } 
    } 
} 

を見てみましょう。つまり、アクションのために期待どおりに実行するモックリーダーを渡すことを意味します。嘲笑読者は、テスト対象のメソッド内で呼び出さアクションに渡すことができるパラメータ

.Callback((string s, Parameters p, Action<System.Data.IDataReader>[] a) => { 

    if (a != null && a.Length > 0) { 
     a.ToList().ForEach(callback => callback(mockDataReader.Object)); 
    } 

}) 

で渡さへのアクセスを取得するためにコールバックを使用して

var mapping = new Dictionary<string, string> { 
    { "Col1", "abc" }, 
    { "Col2", "def" } 
}; 

var mockDataReader = new Mock<IDataReader>(); 
mockDataReader 
    .Setup(m => m[It.IsAny<string>()]) 
    .Returns<string>(col => mapping[col]) 
    .Verifiable(); 

この回答は、OPで提供されている例に合わせて調整されているため、特定のシナリオに適用するためにいくつかの変更が必要な場合があります。これは、このような状況に向かうのに十分なはずです。

+0

ありがとう、それは仕事をする – user48408

関連する問題