Test delle unità / test di integrazione API Web con HttpClient in Visual Studio 2013

Sto avendo difficoltà a provare il mio controller API con Visual Studio 2013. La mia unica soluzione ha un progetto Web API e un progetto di test. Nel mio progetto di test, ho un test unitario con questo:

[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().Result; } } 

Continuo a ricevere un 404 . Ho provato a eseguire la mia API con un’istanza di Visual Studio (IIS Express) e a eseguire il debug di questo Test unità in un’altra istanza. Ma senza fortuna. Ho verificato che posso inserire questo URL in un browser (quando Visual Studio esegue il debug) e vedo la mia risposta JSON. Ma non riesco a capire come farlo funzionare con il mio test unitario e HttpClient . Ho cercato di trovare esempi online ma non riesco a trovarne uno. Qualcuno può aiutare?

AGGIORNAMENTO 1: ho provato ad aggiungere in un percorso ma non è successo nulla.

 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] 

Ecco il mio controller API

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

UPDATE Risoluzione: sono riuscito a farlo funzionare se avessi aggiornato HttpClient senza un object HttpServer . Avrei comunque bisogno di avere due istanze di VS in esecuzione. 1 eseguendo il mio codice API e un altro per eseguire il Test unità.

Ecco un metodo di lavoro.

 [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); } } 

Qualcuno sa perché non funziona con HttpServer e HttpConfiguration passati in HttpClient ? Ho visto molti esempi che usano questo.

Facendo riferimento al seguente articolo che ero in grado di fare …

Test di integrazione dell’API Web ASP.NET con l’hosting in-memory

lavorando con HttpServer e HttpConfiguration passati in HttpClient . Nell’esempio seguente ho creato un semplice ApiController che utilizza il routing degli attributi. Ho configurato HttpConfiguration per mappare le rotte degli attributi e poi lo ho passato al nuovo HttpServer . HttpClient può quindi utilizzare il server configurato per effettuare chiamate di test di integrazione al server di test.

 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(); } } } 

Non c’era bisogno di avere un’altra istanza di VS in esecuzione per test di integrazione del controller.

Ha funzionato anche la seguente versione semplificata del test

 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); } } 

Nel tuo caso devi assicurarti di configurare correttamente il server in modo che corrisponda alla tua configurazione API web. Ciò significherebbe che devi registrare i tuoi percorsi API con l’object HttpConfiguration .

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

Implementazione con test GET / POST che ho usato –

 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(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().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);