2017-02-07 6 views
1

私はJavascriptでバックボーンモデルをテスト駆動しています。そのため、ユーザーが[保存]ボタンをクリックすると、適切なPOSTリクエストがASP.NET MVCアプリケーションに送信されているはずです。これは、このモデルのための最終的な統合状試験(this.serverFake server from Sinon.JSである)である:要求コンテンツからインスタンス化とアクション呼び出しを使用してMVCコントローラをテストするにはどうすればよいですか?

it('should properly formulate request to save data', function() { 
    this.model.data = [{ id: 1, type: 'type', value: 'value' }]; 

    this.model.save(); 

    expect(this.server.requests.length).toEqual(1); 
    expect(this.server.requests[0].method).toEqual('POST'); 
    expect(this.server.requests[0].url).toEqual('MyController/SaveData'); 
    expect(this.server.requests[0].requestHeaders['Content-Type']) 
     .toEqual('application/json;charset=utf-8'); 
    expect(this.server.requests[0].requestBody) 
     .toEqual('[{"id":1,"type":"type","value":"value"}]'); 
}); 

は、今私は、コントローラをテストドライブしたいです。 SaveDataアクション(これは簡単です)を正しく実装していることを確認したいだけですが、リクエストボディからアクション引数へのマッピングとMVCルートからのマッピングが意味を成していることを確認したいと思います。

私は例えば、スタブ HttpContextBaseらconsortesとのユニットテストのコントローラに関する多くの質問を見つけた

:残念ながら、彼らはすべてのコントローラとコールをインスタンス化

アクションメソッドを手動で実行します。これは私にとっては不満です:私は、JSから出てくる(そして前述のユニットテストによって守られている)同じリクエストコンテンツが、ASP.NETアプリケーション側で正しく動作することを主張したいと思います。

私が現在持っているのは、動作させ問題を説明するための草案です。私はスタブとモックのためにRhino Mocksを使用しています。特に、dataWebServiceはアサーションに使用したいモックです。私はテストのポイントが何であるかを明確にするためにそれを含めてきましたが、一般的に問題とは無関係です。私は、これがユニットテストの厳密な定義に一致し、どこかでないことを知って、今

[Test] 
public void GivenNoData_WhenPostingData_ThenCallsWebServiceSaveData() 
{ 
    var httpContext = MockRepository.GenerateStub<HttpContextBase>(); 
    var httpRequest = MockRepository.GenerateStub<HttpRequestBase>(); 

    httpRequest 
     .Stub(hr => hr.Url) 
     .Return(new Uri("http://localhost/MyController/SaveData")); 
    httpRequest 
     .Stub(hr => hr.Headers) 
     .Return(new WebHeaderCollection() 
     { 
      { "X-Requested-With", "XMLHttpRequest" }, 
      { "Content-Type", "application/json;charset=utf-8" } 
     }); 
    httpRequest 
     .Stub(hr => hr.RequestType) 
     .Return("POST"); 

    var requestBody = @"[{""id"":1,""type"":""type"",""value"":""value""}]"; 
    httpRequest 
     .Stub(hr => hr.InputStream) 
     .Return(new MemoryStream(Encoding.UTF8.GetBytes(requestBody))); 

    httpContext.Stub(hc => hc.Request).Return(httpRequest); 

    // The problem starts here 
    // I want MVC to instantiate the controller based on the request 

    var controller = new MyController(dataWebService); 
    controller.ControllerContext 
     = new ControllerContext(httpContext, new System.Web.Routing.RouteData(), controller); 

    dataWebService.Expect(dws => dws.SaveData(Arg<Data>.Matches(/*...*/)); 

    // Second part of the problem, I want MVC to invoke SaveData with arguments 
    // generated from request's body 
    controller.SaveData(/* arguments */); 

    dataWebService.VerifyAllExpectations();  
} 

:問題は、(コントローラのインスタンスとアクションの呼び出し)二重であり、次のコードのコメントで示されユニットテストとインテグレーションテストの間にあります。

しかし、まず、プロセス全体が上から下に向かってテストされていることを確信したいと思います。そして定義について心配します(おそらくコントローラを単体テストに分割し、ルーティングとコントローラの引数解析のための統合的なテスト)。

また、MVCが正しく動作すると仮定すると、自分のコード、特にSaveDataメソッドシグネチャとMVCルート構成だけをテストすることに注意してください。

答えて

1

これは、私にとっては統合テストのようです。とにかくRhinoMockを忘れてしまったのは、他の方法はないのでここで独自のテストスイートを作成するからです。私たちの場合、実際にはHttpClientを使用してcontroller/apiを呼び出し、アクションに必要なurlと引数を渡し、検証の結果を予期します。

public class ClientHandler 
{ 
    private HttpClient _client; 

    public ClientHander() 
    { 
     // add handler if needed ex var handler = new HttpClientHandler() 

     _client = new HttpClient(/*handler*/); 

     // add default header if needed client.DefaultRequestHeaders 
    } 

    public HttpResponseMessage Post(string path, HttpContent content) 
    { 
     // You can async if you want 
     return _client.Post(path, content).Result; 
    } 
} 

これで実際のテストで使用できます。

[TestClass] 
public class MyController 
{ 
    [TestMethod] 
    public void TestMyControllerActionSaveData() 
    { 
     var client = new ClientHandler(); 
     var content = new StringContent(dataHere, Encoding.UTF8, "application/json"); 
     var outPut = client.Post("http://server/action/here", content).Result; 

     // validate expected output here 
    } 
} 

ここには多くの設定とセットアップがありませんが、要点はあります。

更新:これはContinues Integration(CI)の一部としての強力なツールであるため、テストの名前であなたが現在行っていることが本当に気に入りました。あなたのテストメソッドに名前を付ける方法を少しマイナーコメントします。私はテストメソッドの名前をあなたがやりたいアクションに変更して、Gherkin way of BDDDan Northのようなテストの中に入れておくことを提案します。

[TestMethod] 
public void Should_Save_If_Has_Data() 
{ 
    Given(Web_Service_Instance) 
    With(Data_To_Pass) 
    When(Posting_Data_To_Service) 
    Then(Data_Will_Be_Saved) 
    Verify() 
} 

[TestMethod] 
public void Should_Not_Save_If_No_Data() 
{ 
    ..... 
} 

それ前述したようにあなたは、テストスイートを作成することができた場合は、長期的にあなたの利益の多くを与えると、コード(DRY)の繰り返しを避けることができます。また、これらのテストは、Robert C. Martin & Micah Martinで説明されているように、生きた文書として役立ちます。私が関与しているチームに特別な感謝と称賛があります!

+0

問題ありません。それらは単なる提案です。その日の終わりには、あなたやチームや開発のためのよりよいアプローチが何であるかをまだ知っています:) – jtabuloc

+0

テスト用にあなたのウェブアプリケーションをどのようにホストしていますか? – BartoszKP

+0

すべてのコードが統合される専用のテストサーバーが必要です。 TFSをリポジトリとして使用した場合は、すべてのチェックイン(クライアントの環境のように実際に展開する)で、更新、セットアップ、データベースの移行などを行うテストサーバーを指すビルド定義を作成できます。彼らがどのようにそれを行うのかについてGITかSVNでは分かりませんが、それはTFSのためです。 – jtabuloc

0

jtabulocは、これが間違いなく統合テストの問題であることを正しく指摘しています。 ASP.NET MVCスタックの実行に関する要件と同時に、内部WebServiceがスタブされていることは、たとえそれがある意味であっても、達成するのは非常に難しいことです。

私が学んだことから、Webアプリケーションの統合テストにカプセル化された環境(つまり、専用サーバーなし)を提供する唯一の方法は、OWINまたはASP.NET Coreのいずれかを使用することです。現在、MVC 4およびMVC 5アプリケーションのアップグレードが必要なため、これらのどれも私にとって実行可能なものではありません。

これは現時点ではコストがかかりません - データベースとWebServiceの統合テストとユニットテストのコントローラとビューに固執します。

関連する問題