2014-05-07 9 views
5

Ich möchte "Einheit" Test eine Methode auf meinem WebAPI-Controller.Unit testet eine WebAPI2-Controller-Methode mit einem Header-Wert

Diese Methode beruht darauf, dass ein Header gesendet wird.

So

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

benötigt einen Wert in der Methode Körper haben.

Was ist der beste Weg, dies zu tun? Ich dachte, ich könnte den ControllerContext setzen, der HttpContext auffüllen würde, aber es funktioniert nicht.

Ich würde es vorziehen, kein Mocking-Framework oder andere Tools von Drittanbietern zu verwenden, da mein Verständnis ist, dass WebAPI2 gut mit diesem Anwendungsfall spielt.

Ich bin glücklich, HttpContext.Current zu setzen, wenn das der beste Weg ist.

Antwort

5

Hinweis: Diese Antwort funktioniert für den generischen Titel der Frage, jedoch in diesem speziellen Fall hat der Benutzer externen Code, der auf HttpContext.Current beruht, die außerhalb seiner Kontrolle ist. Wenn dies auch der Fall ist, ist das nicht der richtige Weg. Für die meisten anderen Benutzer wird dies weiterhin empfohlen

Verlassen Sie sich nicht auf HttpContext.Current in WebAPI. Im Allgemeinen wird empfohlen, die Verwendung in WebAPI zu vermeiden. Einer der Hauptgründe ist die Einheitentestbarkeit.

Beachten Sie auch, dass ich eine IHttpActionResult zurücksende, die das Testen noch einfacher macht.

Stattdessen benutzen Sie einfach den Controller Mitglied Request.Headers und dann können Sie es über das Kontextobjekt im Testset

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

Hier ist ein Beispiel für eine vollständige End-End-Integrationstestspeicher (wieder, dass Sie beachten . muß, um die Anfrage Eigenschaft verwenden, die in der gesamten Pipeline statt HttpContext.Current verfügbar Dieser Code wurde entnommen aus: WebAPI tests es ein paar mehr Arten von Integrationstests im Code

// 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

Danke! Ich habe versucht, dies zu setzen, aber Request ist null. Ich habe versucht: controller.Request.Headers.Add ("value", "12345"); –

+0

Ich habe eine Probe hinzugefügt. Die Anfrage wird nur zur Ausführungszeit gestellt, also müssen Sie sie zu Testzwecken selbst einrichten. –

+0

Danke, mein Herr. Das ist genau so einfach, wie ich es erwartet habe. Leider habe ich auch erwartet, dass dies HttpContext.Current.Request.Headers auffüllt, aber das tut es nicht. Ist es weg, um dies in den Kontext zu propagieren, so dass mein "Unit" -Test stromabwärts funktioniert. (Ja, es ist eher ein Integrationstest). –

1

Sie verspotten. der HTTP-Anforderungskontext Verwenden Sie ein spöttisches Framework wie Moq? Das macht es leicht, die Anforderungsheadersammlung zu mockern. Wenn Sie nicht Moq verwenden, finden Sie in diesem SO question.

+0

Danke für die Antwort! Ich versuche nicht, ein spöttisches Framework zu verwenden, aber es sieht so aus, als müsste ich es vielleicht tun. Danke für die Ressource. –

+0

Warum also der Downvote? – abjbhat

+0

Huh? Oh Mann, keine Ahnung. Ich habe dich nicht abgelehnt. Muss ein Idiot gewesen sein. Die Leute scheinen es zu lieben, Downvotes auszugeben - ich habe vor einer Weile eine Frage gestellt, die wie 5 Downvotes geht, aber niemand hat sich die Mühe gemacht, mir zu sagen, warum. Hier - haben Sie eine Verbesserung von mir. –

17

Hallo ich könnte ein wenig zu spät zur Party sein, aber ich lief auf das gleiche Problem und ich bin hier, was ich getan habe.

Wie andere bemerkt haben, verwenden Sie Request.Headers anstelle von HttpCurrentContext in Ihren Controller-Aktionen, z.

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

Es ist dann einfach eine HttpControllerContext und das Anforderungs Eigenschaft wie folgt zu erstellen:

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

this helps :)

P. S. Vergessen Sie nicht, Web.Api hinzuzufügen.Core Referenz auf das Testprojekt :)

6

Manchmal haben Sie wenig/keine Kontrolle über den Code, für den Sie Tests schreiben. Wenn es bereits entwickelt wurde, um HttpContext.Current zu verwenden, und Sie erhalten "Operation is not supported on this platform." Fehler wie ich kämpfte, wird dies helfen.

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

Mit dieser Klasse im gleichen Namensraum, können Sie die Header wie hinzufügen:

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

Natürlich, wenn Sie nicht über Erweiterungsmethoden mögen, könnten immer eine Wrapper-Methode stattdessen verwenden.

Ich hoffe, das hilft jemandem.

+1

Beste Antwort hier, da alle anderen Antworten davon ausgehen, dass Sie einen controllerContext verwenden, was nicht immer der Fall ist. Also +1 von einer Person, die MVC im Moment nicht benutzt. –

+0

+1 für die Bereitstellung der bei weitem einfachste Lösung, die nicht auf externe Bibliotheken angewiesen ist und immer funktioniert –

+1

Brilliant hack. Für ServerVariables musste ich t.InvokeMember verwenden ("AddStatic", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, Binder: null, Ziel: Header, args: neues Objekt [] {Schlüssel, Wert}); anstelle von BaseAdd –

Verwandte Themen