10

Ho difficoltà a provare il controller API con Visual Studio 2013. La mia soluzione One ha un progetto API Web e un progetto Test. Nel mio progetto di test, ho una prova di unità con questo:Test delle unità/test di integrazione API Web con HttpClient in Visual Studio 2013

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

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?

UPDATE 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: ero in grado di farlo funzionare se nuove fino HttpClient senza un oggetto 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 il motivo per cui non funziona con un HttpServer e HttpConfiguration passato in HttpClient? Ho visto molti esempi che usano questo.

+1

non potrebbe fare la differenza, ma perché non utilizzare [GetAsync] (https://msdn.microsoft.com/en-us/ library/hh158944 (v = vs.118) .aspx) direttamente? –

+0

Stai usando la pipeline OWIN con l'avvio? Potrebbe essere necessario usare 'TestServer'. https://blogs.msdn.microsoft.com/webdev/2013/11/26/unit-testing-owin-applications-using-testserver/ – Nkosi

+0

Non sto utilizzando owin. – duyn9uyen

risposta

17

'un riferimento al seguente articolo sono stato in grado di fare ...

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

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

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 è necessario eseguire un'altra istanza di VS per eseguire l'integrazione di test del controller.

La seguente versione semplificata del test ha funzionato così

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 è necessario assicurarsi che si sta configurando il server corretto per abbinare il vostro web di configurazione api. Ciò significa che devi registrare i tuoi percorsi API con l'oggetto HttpConfiguration.

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

Esiste comunque la possibilità di inviarmi una copia di questa soluzione? Ho copiato letteralmente il codice ma non sembra funzionare nella mia soluzione. Quindi penso che abbia qualche configurazione diversa. Le proprietà del mio progetto hanno il progetto in esecuzione sotto IIS Express sotto un numero di porta. L'url del progetto ha questo "http: // localhost: 50892 /." Ho provato a utilizzare la porta e senza il numero di porta nel mio test di unità, ma senza successo. Ancora 404. – duyn9uyen

+0

Con le richieste di test in memoria non si sta effettivamente colpendo la rete, quindi non c'è nulla a che fare con IIS o IIS Express. 'HttpServer' a' DelegatingHandler' che sta gestendo la richiesta che 'HttpClient' sta facendo. Non c'è bisogno di una porta e il default è 'http: // localhost' come nei miei esempi. Ho preso il codice esatto dal tuo post, modificato solo l'host dell'URL e configurato il controller per utilizzare il routing degli attributi. – Nkosi

+0

Copia la classe di test unità esatta come è nella mia risposta ed eseguila nel tuo progetto di test. Dovrebbe funzionare e passare. È quindi possibile rivedere come è stato fatto e provare ad applicare la stessa tecnica al test effettivo. Non c'era niente in più. Tutto ciò che vedi è esattamente come è stato scritto e testato. – Nkosi

-3

Attuazione con test GET/POST 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<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);