2014-05-07 15 views
5

Vorrei provare a "unità" un metodo sul mio contoller WebAPI.Unità che esegue il test di un metodo di controllo WebAPI2 con un valore di intestazione

Questo metodo si basa su un'intestazione inviata con esso.

Così

HttpContext.Current.Request.Headers["name"] 

ha bisogno di avere un valore nel corpo del metodo.

Qual è il modo migliore per farlo? Ho pensato che sarei stato in grado di impostare il ControllerContext che avrebbe popolato HttpContext, ma non può farlo funzionare.

Preferirei non utilizzare un framework di simulazione o altri strumenti di terze parti, poiché la mia comprensione è che WebAPI2 funziona bene con questo caso d'uso.

Sono felice di impostare HttpContext.Current se questo è il modo migliore.

risposta

5

Nota: questa risposta funziona per il titolo generico della domanda, tuttavia in questo caso l'utente ha un codice esterno che si basa su HttpContext.Current fuori dal suo controllo. Se anche questo è il tuo caso, questa non è la strada da percorrere. Per la maggior parte degli altri utenti questo è comunque raccomandato

Non fare affidamento su HttpContext.Current in WebAPI. In generale, è consigliabile evitare di utilizzarlo in WebAPI, uno dei motivi principali è la testabilità dell'unità.

Nota anche che sto restituendo uno IHttpActionResult che renderà il test ancora più semplice.

Invece basta utilizzare il membro del controller Request.Headers e quindi è possibile impostare tramite l'oggetto contesto nel tuo test

public class MyController : ApiController 
{ 
    public IHttpActionResult Get() 
    { 
     if (Request.Headers. /* insert your code here */) 
     { 
      // Do Something 
     } 
    } 
} 

public class TestClass 
{ 
    public void Test() 
    { 
     // Arrange 
     var controller = new MyController(); 
     var request = new HttpRequestMessage(); 
     request.Headers... // setup your test here 

     // Act 
     var result = controller.Get(); 

     // Assert 
     // Verify here 
    } 
} 

Ecco un esempio per una completa end-end in test di integrazione di memoria (ancora una volta notare che è . bisogno di utilizzare la proprietà richiesta che è disponibile in tutta la pipeline piuttosto che HttpContext.Current Questo codice è stato preso da: WebAPI tests c'è qualche altro stili di test di integrazione nel codice

// Do any setup work 
HttpConfiguration config = new HttpConfiguration(); 
config.Routes.MapHttpRoute("Default", "{controller}/{action}"); 

// Setup in memory server and client 
HttpServer server = new HttpServer(config); 
HttpClient client = new HttpClient(server); 

// Act 
HttpResponseMessage response = client.GetAsync("http://localhost/" + requestUrl).Result; 

// Assert 
Assert.Equal(HttpStatusCode.OK, response.StatusCode); 
Assert.Equal(count, response.Content.ReadAsAsync<int>().Result); 
+0

Grazie! Ho provato a impostarlo, ma Request è nullo. Ho provato: controller.Request.Headers.Add ("valore", "12345"); –

+0

Ho aggiunto un campione. La richiesta viene impostata solo al momento dell'esecuzione, quindi per scopi di test è necessario configurarlo autonomamente. –

+0

Grazie signore. Questo è esattamente quanto mi aspettavo che fosse. Sfortunatamente mi aspettavo anche che questo popolasse HttpContext.Current.Request.Headers, ma non è così. C'è via per farlo propagare nel contesto in modo che il mio test "unità" funzioni a valle. (sì, è più di un test di integrazione). –

1

è possibile prendere in giro. il contesto della richiesta HTTP. Stai usando una struttura beffarda come Moq? Ciò semplifica la raccolta delle intestazioni delle richieste. Se non si utilizza Moq, vedere questo SO question.

+0

Grazie per la risposta! Sto cercando di non usare un sistema di derisione, ma sembra che potrei doverlo fare. Grazie per la risorsa. –

+0

Allora perché il downvote? – abjbhat

+0

Eh? Oh uomo, non ne ho idea. Non ti ho votato. Deve essere stato un idiota. La gente sembra amare i downvotes - ho fatto una domanda un po 'indietro che è andata come 5 downvotes ma nessuno si è preoccupato di dirmi perché. Qui - avere un upvote da me. –

17

Ciao, potrei essere un po 'in ritardo per la festa, ma mi sono imbattuto nello stesso problema e io qui è quello che ho finito per fare.

Come altri hanno notato, utilizzare Request.Headers anziché HttpCurrentContext nelle azioni del controllore, ad es.

[Route("")] 
    [HttpGet] 
    public IHttpActionResult Get() 
    { 
     // The header can have multiple values, I call SingleOrDefault as I only expect 1 value. 
     var myHeader = Request.Headers.GetValues("X-My-Header").SingleOrDefault(); 
     if (myHeader == "success") 
     { 
      return Ok<string>("Success!"); 
     } 

     return BadRequest(); 
    } 

È allora davvero facile creare un HttpControllerContext e impostare la proprietà richiesta in questo modo:

[TestMethod] 
public void Get_HeaderIsValid() 
{ 
    // Arrange 
    var controller = new ConfigurationsController(null); 
    var controllerContext = new HttpControllerContext(); 
    var request = new HttpRequestMessage(); 
    request.Headers.Add("X-My-Header", "success"); 

    // Don't forget these lines, if you do then the request will be null. 
    controllerContext.Request = request; 
    controller.ControllerContext = controllerContext; 

    // Act 
    var result = controller.Get() as OkNegotiatedContentResult<string>; 

    // Assert 
    Assert.IsNotNull(result); 
    Assert.AreEqual("Success!", result.Content); 
} 

Spero che questo aiuti :)

P.S. Non dimenticare di aggiungere Web.Api.Core Riferimento al progetto di test :)

6

A volte, il controllo del codice per il quale si sta scrivendo il test è limitato o inesistente. Se è già stato progettato per utilizzare HttpContext.Current e continui a ricevere errori "Operation is not supported on this platform." come se fossi in difficoltà, ciò sarà di aiuto.

public static class NameValueCollectionExtensions 
{ 
    public static NameValueCollection AddValue(this NameValueCollection headers, string key, string value) 
    { 
     Type t = headers.GetType(); 
     t.InvokeMember("MakeReadWrite", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, headers, null); 
     t.InvokeMember("InvalidateCachedArrays", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, headers, null); 
     t.InvokeMember("BaseAdd", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, headers, new object[] { key, new System.Collections.ArrayList() { value } }); 
     t.InvokeMember("MakeReadOnly", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, headers, null); 
     return headers; 
    } 
} 

Con quella classe nello stesso spazio dei nomi, è possibile aggiungere le intestazioni come:

HttpContext.Current.Request.Headers.AddValue("header_key", "header_value"); 

Naturalmente, se non ti piace metodi di estensione, si può sempre usare un metodo involucro, invece.

Spero che questo aiuti qualcuno.

+1

La migliore risposta qui dato che tutte le altre risposte presuppongono che tu stia utilizzando un controllerContext, il che non è sempre il caso. Quindi +1 da una persona che non utilizza MVC al momento. –

+0

+1 per fornire di gran lunga la soluzione più semplice che non dipende da alcuna libreria esterna e funziona sempre con –

+1

Brilliant hack. Per ServerVariables dovevo usare t.InvokeMember ("AddStatic", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, binder: null, target: headers, args: new object [] {key, value}); invece di BaseAdd –

Problemi correlati