2010-12-10 4 views
2

私はViewDataにデータを入れるコントローラをユニットテストしようとしています。我々の見解はすべて、同様のデータ(URLから派生した顧客情報)を必要とします。そのため、元の開発者は、この呼び出しをすべての単一のコントローラメソッドに入れるのではなく、このViewDataをOnActionExecutingイベントに入れることを選択しました。ユニットテストでASP.NET MVC2カスタムビューエンジンが無視される

もちろん、ユニットテストからコントローラのアクションを呼び出すと、OnActionExecutingは起動しません。 (ありがとうMVCチーム!)

私はカスタムビューエンジンを作成し、ビューが要求されたときに顧客データをcontrollerContextに入れることを試みました。これはブラウザで正常に動作しますが、このテストを実行すると私のviewEngineは無視されます。 ViewEngines.Add(new funkyViewEngine)の効果はありません。

[TestMethod()] 
      public void LoginTest() 
      { 
       ViewEngines.Engines.Clear(); 
       ViewEngines.Engines.Add(new FunkyViewEngine()); 

       UserController target = new UserController(); 
       target.SetStructureMap(); <--sets us to use the test repo 

       target.ControllerContext.HttpContext = MVCHelpers.FakeHttpContext("https://customersubdomain.ourdomain.com"); <--moq magic 
       var actual = target.Login(); 
       Assert.IsTrue(actual.GetType().IsAssignableFrom(typeof(System.Web.Mvc.ViewResult))); 
       var vr = actual as ViewResult; 
       Assert.IsTrue(vr.ViewData.Community() != null); <--"Community" should be set by viewengine 
       Assert.IsTrue(vr.ViewData.Community().Subdomain == "customersubdomain.ourdomain"); 
       Assert.IsTrue(vr.ViewData.Community().CanRegister); 
      } 

ここに希望がありますか? 1)ブラウザとユニットフレームワークの両方でコントローラを実行するために呼び出されるメソッドを作成するか、2)ビューフレームを呼び出すユニットフレームワークを取得するかのどちらかです。

答えて

2

申し訳ありません。ユニットテストからアクションメソッドを直接呼び出すときにOnActionExecutingが呼び出されていないのは、それがMVCでの動作ではないからです。

要求は、パイプラインを通じて実行されます。このパイプラインは、この領域についてはControllerActionInvokerです。このクラスは、責任がある:アクションメソッド自体

  • 呼び出す
  • :アクションフィルタOnActionExecuting方法(あなたのコントローラクラスもアクションフィルタであるノート)を呼び出すアクションメソッド
  • を見つける

    1. アクションフィルタを呼び出すOnActionExecutedメソッド
    2. 結果を処理する(たとえば、ビューを見つけてレンダリングする)

    ユニットテストでは、ステップ3を直接呼び出して、他のすべてのステップをスキップします。単体テストでは、あなたのアクションが動作するのに必要なセットアップコードを呼び出すことはあなたの責任です。

    ただし、パイプライン全体を実行するためにControllerActionInvokerを使用する単体テストを作成する必要はありません。私たち(MVCチーム)は、すべての作品が協力し合っていることを既に確認しています。

    代わりに、特定のアプリケーションコードをテストする必要があります。

    1. あなたのコントローラ上OnActionExecutingを呼び出す与えられたいくつかのURLは、いくつかの顧客与えられたことを確認するテスト
    2. ViewDataを
    3. に右Customerオブジェクトを置くことを確認するテストを:この場合は、次のユニットテストを持っ検討するかもしれませんViewDataに存在するオブジェクトあなたのアクションメソッドは適切な結果を返します

    私の最後のポイントは、機能をOnActionExecutingに保持することです。カスタムビューエンジンは確かに間違った場所です。

  • +0

    ええ、ViewEngineのことは、この動作によってすべてが簡単なコントローラを完全にテストできないようにするというハックでした。私はOnActionExecutingとOnActionExecutedを呼び出すPublicラッパーを置いて手動で呼び出すことができますが、それはRoleFilter、SessionFilter、RequireHttpsなどの属性に役立ちません。 –

    +0

    アプリケーションと信頼関係を構成するコンポーネントを個別にテストする必要があります:)フレームワークはすべてが一緒に働くようにします。たとえば、フィルター属性の場合、リフレクションを使用して、属性がメソッドに宣言されていることを確認することができます。 Mvcはテスト可能に設計されており、難しいのはフレームワーク自体をテストしようとするときだけです。このような問題にぶつかると、別のアプローチを取らなければならないかもしれないという良い指標です。 – marcind

    +0

    MyController.Actionがhttpをhttpsに正しくリダイレ​​クトしてユーザーをログインさせることを確認するために単体テストを使用できないということはまだ完全に壊れています。チームを蹴ってTDDに向かって叫んでいる人は、MVCチームがテストを邪魔しています。 –

    0

    おそらくあなたが探している答えではありませんが、同じ目標(マルチテナントアプリでURLから顧客を取得する)を達成するためにカスタムMvcHandlerを使用しています。 ViewEngineは

    マイカスタムハンドラは、多かれ少なかれ、このようになります...私にはこの種のロジックのために良い場所のような音はありません:あなたの欲求不満のため

    public class AccountMvcHandler : MvcHandler 
    { 
        public Account Account { get; private set; } 
    
        protected override IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state) 
        { 
         return base.BeginProcessRequest(httpContext, callback, state); 
        } 
    
        protected override IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state) 
        { 
         string accountName = this.RequestContext.RouteData.GetRequiredString("account"); 
         Account = ServiceFactory.GetService<ISecurityService>().GetAccount(accountName); 
    
         return base.BeginProcessRequest(httpContext, callback, state); 
        } 
    } 
    
    +0

    少しわかりますか? ViewEngineのハックは単体テストのアプローチでのアクション配線の欠如の醜いフォールバックです。もっと良い方法があれば、聞いてみたいと思います。 –

    関連する問題