2017-03-23 25 views
2

私がテストしたいメソッドの内部で具体化された具体的なクラスを持つこのような設定があります。私はこの具体的なクラスを模擬し、コードを内部で実行しないようにしたい。そのため、例外がスローされるべきではない:具体的なクラスのメソッド呼び出しを呼び出す

public class Executor 
{ 
    public bool ExecuteAction(ActionRequest request) 
    { 
     switch (request.ActionType) 
     { 
      case ActionType.Foo: 
       var a = new Foo(); 
       return a.Execute(request); 
      case ActionType.Bar: 
       var b = new Bar(); 
       return b.Execute(request); 
     } 

     return true; 
    } 
} 

public class Foo 
{ 
    public virtual bool Execute(ActionRequest request) 
    { 
     throw new NotImplementedException(); 
    } 
} 

public class Bar 
{ 
    public virtual bool Execute(ActionRequest request) 
    { 
     throw new NotImplementedException(); 
    } 
} 

私のNUnitのテストは次のようになります。

[Test] 
public void GivenARequestToFooShouldExecuteFoo() 
{ 
    var action = new Mock<Foo>(); 
    action.Setup(x => x.Execute(It.IsAny<ActionRequest>())).Returns(true); 

    var sut = new Mock<Executor>(); 
    sut.Object.ExecuteAction(new ActionRequest 
    { 
     ActionType = ActionType.Foo 
    }); 
} 

[Test] 
public void GivenARequestToBarShouldExecuteBar() 
{ 
    var action = new Mock<Bar>(); 
    action.Setup(x => x.Execute(It.IsAny<ActionRequest>())).Returns(true); 

    var sut = new Mock<Executor>(); 
    sut.Object.ExecuteAction(new ActionRequest 
    { 
     ActionType = ActionType.Bar 
    }); 
} 

私はCallBaseの周りいじっが、それはどこでも私を取得できませんでした。とにかく、これらのクラスの依存性注入やインターフェイスの追加がなければ、これを簡単に解決できますか?これはMoqを使って可能ですか?

ExecuteメソッドをExecutorクラスに移動し、ExecuteFoo()ExecuteBar()に名前を変更するだけですが、移動するコードがたくさんあるので、部分クラス(サブクラス?)。

+1

'Executor.ExecuteAction'メソッドを' virtual'にするとできます。しかし、テストが設計上の欠陥を検出する方法の1つであるという事実を考えれば、あなたが持っているものを再加工することをお勧めします。あなたが示したテストは実装との緊密な結合のために非常に脆弱であることは言うまでもありません(実際にここで 'switch'ステートメントをテストしていますか?) –

答えて

3

問題は方法の模擬ではなく、具体的なクラスの作成にあります。 FooBarの作成はExecutorから逆にする必要があります。アクションを実行し、作成しないでください。このインターフェイスは作成を処理するために作成されました。

public interface IActionCollection : IDictionary<ActionType, Func<IExecute>> { 

} 

これを工場のコレクションまたは作成戦略のコレクションと考えてください。

アクション用の共通インターフェイスが作成されました。

public interface IExecute { 
    bool Execute(ActionRequest request); 
} 

public class Foo : IExecute { 
    public virtual bool Execute(ActionRequest request) { 
     throw new NotImplementedException(); 
    } 
} 

public class Bar : IExecute { 
    public virtual bool Execute(ActionRequest request) { 
     throw new NotImplementedException(); 
    } 
} 

そして、Executorは、依存性反転を使用するようにリファクタリングされました。

public class Executor { 
    readonly IActionCollection factories; 

    public Executor(IActionCollection factories) { 
     this.factories = factories; 
    } 

    public bool ExecuteAction(ActionRequest request) { 
     if (factories.ContainsKey(request.ActionType)) { 
      var action = factories[request.ActionType](); 
      return action.Execute(request); 
     } 
     return false; 
    } 
} 

そのリファクタを実行すると、実行者は偽の操作でテストできます。

public void GivenARequestToFooShouldExecuteFoo() { 
    //Arrange 
    var expected = true; 
    var key = ActionType.Foo; 

    var action = new Mock<Foo>(); 
    action.Setup(x => x.Execute(It.IsAny<ActionRequest>())).Returns(expected); 

    var actions = new Mock<IActionCollection>(); 
    actions.Setup(_ => _[key]).Returns(() => { return() => action.Object; }); 
    actions.Setup(_ => _.ContainsKey(key)).Returns(true); 

    var sut = new Executor(actions.Object); 
    var request = new ActionRequest { 
     ActionType = ActionType.Foo 
    }; 

    //Act 
    var actual = sut.ExecuteAction(request); 

    //Assert 
    Assert.AreEqual(expected, actual); 
} 

工場コレクションの生産の実装では、この

public class ActionCollection : Dictionary<ActionType, Func<IExecute>>, IActionCollection { 
    public ActionCollection() 
     : base() { 
    } 
} 

とあなたの具体的な種類に応じて構成されたように見えることができます。

var factories = ActionCollection(); 
factories[ActionType.Foo] =() => new Foo(); 
factories[ActionType.Bar] =() => new Bar(); 
関連する問題