10

Visual Studio 2013でAPIコントローラをテストしようとしています。私のソリューションの1つにWeb APIプロジェクトとテストプロジェクトがあります。私は404を得続けるVisual Studio 2013でHttpClientを使用したユニットテスト/統合テストWeb API

[TestMethod] 
public void GetProduct() 
{ 
    HttpConfiguration config = new HttpConfiguration(); 
    HttpServer _server = new HttpServer(config); 

    var client = new HttpClient(_server); 

    var request = new HttpRequestMessage 
    { 
     RequestUri = new Uri("http://localhost:50892/api/product/hello"), 
     Method = HttpMethod.Get 
    }; 

    request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); 

    using (var response = client.SendAsync(request).Result) 
    { 
     Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); 

     var test = response.Content.ReadAsAsync<CollectionListDTO>().Result; 
    } 
} 

:私のテストプロジェクトでは、私はこれでユニットテストを持っています。 Visual Studio(IIS Express)の1つのインスタンスでAPIを実行し、このユニットテストを別のインスタンスでデバッグしようとしました。しかし、運がない。私はブラウザにこのURLを置くことができることを確認しました(1つのVisual Studioがデバッグしているとき)、JSON応答が表示されます。しかし、私はユニットテストとHttpClientで動作させる方法を理解できません。私は例をオンラインで見つけようとしましたが、見つけられないようです。助けてもらえますか?

更新1: ルートで追加しようとしましたが、何も起こりませんでした。私はHttpServerオブジェクトなしHttpClientをアップし、新たな場合、私はそれが仕事を得ることができた

HttpConfiguration config = new HttpConfiguration(); 

// Added this line 
config.Routes.MapHttpRoute(name: "Default", routeTemplate: "api/product/hello/"); 

HttpServer _server = new HttpServer(config); 

var client = new HttpClient(_server); 

[...rest of code is the same] 

は、ここに私のAPIコントローラ

[HttpGet] 
[Route("api/product/hello/")] 
public IHttpActionResult Hello() 
{ 
    return Ok(); 
} 

UPDATE解像度です。私はまだVSの2つのインスタンスを実行する必要があります。 1は私のAPIコードを実行し、もう1つは単体テストを実行します。

ここには動作する方法があります。

[TestMethod] 
public void Works() 
{ 
    var client = new HttpClient(); // no HttpServer 

    var request = new HttpRequestMessage 
    { 
     RequestUri = new Uri("http://localhost:50892/api/product/hello"), 
     Method = HttpMethod.Get 
    }; 

    request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); 

    using (var response = client.SendAsync(request).Result) 
    { 
     Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); 
    } 
} 

それはHttpServerHttpClientに渡さHttpConfigurationでは動作しない理由を誰もが知っていますか?私はこれを使用する多くの例を見てきました。 HttpServerHttpClientに渡さHttpConfigurationと協力して、私はやることができた次の記事を参照

+1

は違いを生むないかもしれませんが、なぜ[GetAsync](https://msdn.microsoft.com/en-us/を使用しませんライブラリ/ hh158944(v = 118).aspx)を直接? –

+0

起動時にOWINパイプラインを使用していますか? 'TestServer'を使う必要があります。 https://blogs.msdn.microsoft.com/webdev/2013/11/26/unit-testing-owin-applications-using-testserver/ – Nkosi

+0

私はowinを使用していません。 – duyn9uyen

答えて

17

...

ASP.NET Web API integration testing with in-memory hosting

。次の例では、属性ルーティングを使用する単純なApiControllerを作成しました。 HttpConfigurationに属性ルートをマップし、それを新しいHttpServerに渡しました。 HttpClientは、構成済みサーバーを使用して、テスト・サーバーへの統合テスト呼び出しを行うことができます。

public partial class MiscUnitTests { 
    [TestClass] 
    public class HttpClientIntegrationTests : MiscUnitTests { 

     [TestMethod] 
     public async Task HttpClient_Should_Get_OKStatus_From_Products_Using_InMemory_Hosting() { 

      var config = new HttpConfiguration(); 
      //configure web api 
      config.MapHttpAttributeRoutes(); 

      using (var server = new HttpServer(config)) { 

       var client = new HttpClient(server); 

       string url = "http://localhost/api/product/hello/"; 

       var request = new HttpRequestMessage { 
        RequestUri = new Uri(url), 
        Method = HttpMethod.Get 
       }; 

       request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); 

       using (var response = await client.SendAsync(request)) { 
        Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); 
       } 
      } 
     } 
    } 

    public class ProductController : ApiController { 
     [HttpGet] 
     [Route("api/product/hello/")] 
     public IHttpActionResult Hello() { 
      return Ok(); 
     } 
    } 
} 

コントローラーを統合テストするために、VSの別のインスタンスを実行する必要はありませんでした。

テストの以下の簡易版では、あなたが適切にあなたのWeb APIの設定に一致するようにサーバーを設定していることを確認する必要があり、あなたの場合も

var config = new HttpConfiguration(); 
//configure web api 
config.MapHttpAttributeRoutes(); 

using (var server = new HttpServer(config)) { 

    var client = new HttpClient(server); 

    string url = "http://localhost/api/product/hello/"; 

    using (var response = await client.GetAsync(url)) { 
     Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); 
    } 
} 

働いていました。これは、HttpConfigurationオブジェクトでAPIルートを登録する必要があることを意味します。私が使用したGET/POSTテストでの

var config = new HttpConfiguration(); 
//configure web api 
WebApiConfig.Register(config); 
//...other code removed for brevity 
+0

このソリューションのコピーを私に送ってもらえますか?私は文字通りコードをコピーしましたが、私のソリューションではうまくいかないようです。だから私は、いくつかの設定が異なっていると思います。プロジェクトのプロパティには、ポート番号でIIS Expressでプロジェクトが実行されています。プロジェクトのURLには、この "http:// localhost:50892 /"があります。私はポートを使用しようとしましたが、私の単体テストではポート番号は使用しませんでしたが、成功しませんでした。依然として404. – duyn9uyen

+0

メモリ内テスト要求は実際にネットワークに到達していないため、IISやIIS Expressとは関係ありません。 'HttpServer'は、' HttpClient'が行っているリクエストを処理している 'DelegatingHandler'です。私の例のように、ポートは必要なく、デフォルトは 'http:// localhost'です。あなたの投稿から正確なコードを取り出し、URLのホストを変更し、属性ルーティングを使用するようにコントローラを設定しました。 – Nkosi

+0

正確な単体テストクラスをそのまま私の答えにコピーし、テストプロジェクトで実行してください。実行して渡す必要があります。それがどのように行われたのかを見直して、同じテクニックを実際のテストに適用しようとすることができます。余計なことは何もなかった。あなたがそこに見るすべては、それが書かれテストされた方法とまったく同じです。 – Nkosi

-3

実装 -

public static HttpResponseMessage ConfigureAndAct(string relativeApiUrl, HttpMethod httpMethodType, string bodyForPostMethod, int userid) 
    { 
     var uri = new Uri(string.Concat("http://localhost:10022", relativeApiUrl)); // Todo:Find free port at runtime 

     // var identity = new GenericIdentity(userid.ToString()); // If Needed 
     // Thread.CurrentPrincipal = new GenericPrincipal(identity, null); // If Needed 
     HttpResponseMessage response; 

     using (HttpConfiguration config = new HttpConfiguration()) 
     { 
      config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always; 

      // WebApiConfig.Register(config); // If Needed 
      // FilterConfig.RegisterGlobalFilters(GlobalConfiguration.Configuration.Filters); // If Needed 

      // config.Services.Replace(typeof(IHttpControllerSelector), new VersionControllerSelector(config)); // If Needed 

      using (HttpRequestMessage request = new HttpRequestMessage() 
      { 
       Content = new StringContent(bodyForPostMethod, Encoding.UTF8, "application/json"), 
       RequestUri = uri, 
       Method = httpMethodType, 
      }) 
      { 
       // var creds = new CommonSetupRepository().GetUserNameAndPassword(userid); // If Needed 
       // var toekn = Pearl.Utilities.Security.CryptionService.Encrypt<RijndaelManaged>(String.Format("{0}:{1}", creds.UserName, creds.Password)); // If Needed 

       // request.Headers.Add("rb-api-version", "0.0.1"); // If Needed 
       // request.Headers.Add("rb-token", toekn); // If Needed 

       using (var server = new HttpServer(config)) 
       { 
        using (var client = new HttpClient(server)) 
        { 
         response = client.SendAsync(request, CancellationToken.None).Result; 
        } 
       } 
      } 
     } 

     return response; 
    } 




// Test GET 
    ----------- 
    //Act  
var response = ConfigureWebApiForTest.ConfigureAndAct(uriToTest, HttpMethod.Get, string.Empty, userId); 
    var result = response.Content.ReadAsAsync<Activity>().Result; 
    //Assert 
    Assert.IsTrue(response.StatusCode == HttpStatusCode.OK); 
    Assert.IsNotNull(result); 
    Assert.AreEqual(expectedUrl, result.SomeUrl); 

    // Test POST 
    ------------ 
    // Act 
    HttpResponseMessage response = ConfigureWebApiForTest.ConfigureAndAct(uriToTest, HttpMethod.Post, body, userId); 
//Assert 
    Assert.IsTrue(response.StatusCode == HttpStatusCode.OK); 
関連する問題