2009-05-28 7 views
3

は、私は(BuildServerExtensionsクラスで)次のシグネチャを持つ拡張メソッドを持っている::拡張メソッドに依存するメソッドをテスト可能にする方法はありますか?

public static IEnumerable<BuildAgent> GetEnabledBuildAgents(
              this IBuildServer buildServer, 
              string teamProjectName) 
{ 
    // omitted agrument validation and irrelevant code 
    var buildAgentSpec = buildServer.CreateBuildAgentSpec(teamProjectName); 
} 

そして(BuildAgentSelectorクラスの)最初の呼び出しを別の方法:

public BuildAgent Select(IBuildServer buildServer, string teamProjectName) 
{ 
    // omitted argument validation 
    IEnumerable<BuildAgent> serverBuildAgents = 
     buildServer.GetEnabledBuildAgents(teamProjectName); 

    // omitted - test doesn't get this far 
} 

そして、私はしようとしています私は私が手にこのテストを実行すると

[TestMethod] 
public void SelectReturnsNullOnNullBuildAgents() 
{ 
    Mocks = new MockRepository(); 
    IBuildServer buildServer = Mocks.CreateMock<IBuildServer>(); 

    BuildAgentSelector buildAgentSelector = new BuildAgentSelector(); 
    using (Mocks.Record()) 
    { 
     Expect.Call(buildServer.GetEnabledBuildAgents(TeamProjectName)).Return(null); 
    } 

    using (Mocks.Playback()) 
    { 
     BuildAgent buildAgent = buildAgentSelector.Select(buildServer, TeamProjectName); 

     Assert.IsNull(buildAgent); 
    } 
} 

::でMSTestをしてRhino.Mocks(V3.4)を使用してテスト

System.InvalidOperationException:戻り値またはスローする例外を必要とIBuildServer.CreateBuildAgentSpec("TeamProjectName");

前の方法。

これは明らかにテスト実装ではなく実際の拡張メソッドを呼び出しています。私の次の傾きが試してみた:

Expect.Call(BuildServerExtensions.GetEnabledBuildAgents(buildServer, TeamProjectName)) 
     .Return(null); 

その後、私はこれを傍受するRhino.Mocksための私の期待は、おそらく見当違いだったことに気づきました。

問題は次のとおりです。この依存関係をなくし、S​​electメソッドをテスト可能にするにはどうすればよいですか?

拡張メソッドとBuildAgentSelectorクラスは同じアセンブリ内にあり、これを変更することを避けるか、拡張メソッド以外のものに変更する必要があることに注意してください。別のモッキングフレームワークは、この状況。

答えて

3

あなたの拡張メソッドは、実際にはかなりうまく書かれています。その副作用のないメソッドであり、具体的なクラスではなくインターフェイスを拡張しています。あなたはほとんどそこにいますが、ちょっと遠くに行く必要があります。 .GetEnabledBuildAgents(...)拡張メソッドをモックしようとしています...しかし実際にはモック可能ではありません(TypeMockアイソレータ以外の何かによって、現時点で実際に統計を模倣できる唯一のものです...しかし、 。)

実際には、拡張メソッドが内部的に呼び出すIBuildAgentのメソッドを模倣することに興味があります:.CreateBuildAgentSpec(...)。それを考えると、CreateBuildAgentSpecメソッドを嘲笑することで問題は解決します。拡張メソッドは "純粋な"ので、本当に嘲笑する必要はありません。状態はなく、副作用はありません。これは、IBuildAgentインターフェース上で単一のメソッドを呼び出します。これは、あなたが実際に嘲笑される必要があることを指示する最初の手掛かりです。

は、以下のことを試してみてください。

[TestMethod] 
public void SelectReturnsNullOnNullBuildAgents() 
{ 
    Mocks = new MockRepository(); 
    IBuildServer buildServer = Mocks.CreateMock<IBuildServer>(); 

    BuildAgent agent = new BuildAgent { ... }; // Create an agent 
    BuildAgentSelector buildAgentSelector = new BuildAgentSelector(); 
    using (Mocks.Record()) 
    { 
     Expect.Call(buildServer.CreateBuildAgentSpec(TeamProjectName)).Return(new List<BuildAgent> { agent }); 
    } 

    using (Mocks.Playback()) 
    { 
     BuildAgent buildAgent = buildAgentSelector.Select(buildServer, TeamProjectName); 

     Assert.IsNull(buildAgent); 
    } 
} 

BuildAgentインスタンスを作成し、BuildAgent >一覧<にそれを返すことによって、あなたは効果的にあなたの選択方法は、上で動作することができるIEnumerableを<BuildAgent>を返します。それはあなたを得る必要があります。基本的なBuildAgentインスタンスを返すだけでは不十分である場合や、複数のインスタンスが必要な場合に備えて、いくつか追加の模擬処理を行う必要があります。返される結果を嘲笑することになると、Rhino.Mocksは後ろの作業に苦労することがあります。あなたが問題に遭遇した場合(私の経験を考えれば、あなたがそうする可能性が高い)、私はあなたにお勧めしますgive Moq a try、それはより良いとテスターフレンドリーなフレームワークで動作するようです。これはリポジトリを必要とせず、Rhino.Mocksが必要とするRecord/PlaybackとUsing()ステートメントの重い記法を排除します。 MOQはまた、あなたが重いモックシナリオに得れば、あなたと恋に落ちるだろう、他のフレームワークは、まだそれを提供していない追加機能を提供します(すなわち、それは。*メソッド。)このことができます

希望を。

0

このテストでは、実際の拡張メソッドが唯一であるため、このメソッドが呼び出されます。このメソッドがIBuildServerのメンバーではないため、IBuildServerをモックすると、テスト実装は作成されません。

これまでの設定では、これを解決する方法はありません。

理論的には、TypeMockは静的クラスをモックしますが、拡張メソッドをリファクタリングすると、より大きなテスト容易性が得られます。

1

休憩して新鮮な頭で戻ってきたら、私は実際にはBuildAgentSelectorクラスの懸念を少し混ぜていることに気付きました。私はです。エージェントはです。これら2つの懸念事項を分離し、エージェントを渡してBuildAgentSelectorコンストラクタ(またはそのためのデリゲート/インターフェイス)に直接選択することで、懸念事項を分け、buildServerとteamProjectNameパラメータの両方の依存関係を削除し、インターフェイスを単純化することができます過程の中で。また、私がBuildAgentSelectorクラスで探していたテスト容易性の結果も得られました。拡張メソッドを個別にうまくテストできます。

しかし、結局のところ、テスト問題を他の場所に移しただけです。関心事が置かれている方が良いですが、jristaの答えは、問題がどこに置かれていても問題を解決します。

テスト中のコードの下に2番目のレイヤーをモックする必要があるのはまだまだ醜いです。私は基本的に私の拡張メソッドテストから成功した道筋を模索し、このコードを他のテストで再利用する必要があります。難しくありませんが、ちょっと面倒です。

私はMOQを試してみて、拡張メソッドを書くのに慣れてしまうことに注意してください。

関連する問題