10

Ich habe eine harte Zeit zu versuchen, meine API-Controller mit Visual Studio 2013 zu testen. Meine eine Lösung hat ein Web-API-Projekt und ein Test-Projekt. In meinem Test-Projekt habe ich eine Einheit Test mit diesem:Unit Testing/Integration Testing Web API mit 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; 
    } 
} 

Ich erhalte ein 404. Ich habe versucht, meine API mit einer Instanz von Visual Studio (IIS Express) auszuführen und zu versuchen, diesen Komponententest in einer anderen Instanz zu debuggen. Aber kein Glück. Ich habe überprüft, dass ich diese URL in einen Browser setzen kann (wenn ein Visual Studio debuggt) und ich meine JSON-Antwort sehe. Aber ich kann nicht herausfinden, wie man es mit meinem Komponententest und HttpClient zur Arbeit bringt. Ich habe versucht, Beispiele online zu finden, kann aber keine finden. Kann jemand helfen?

UPDATE 1: Ich versuchte, eine Route hinzuzufügen, aber nichts passierte.

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] 

Hier ist mein API-Controller

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

UPDATE Auflösung: Ich war in der Lage, es zu bekommen zu arbeiten, wenn ich HttpClient ohne HttpServer Objekt neu auf. Ich würde immer noch zwei Instanzen von VS laufen lassen müssen. 1 mit meinem API-Code und ein anderer, um den Unit Test zu starten.

Hier ist eine Arbeitsmethode.

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

Wer weiß, warum es nicht mit einem HttpServer funktioniert und ein HttpConfiguration in HttpClient weitergegeben? Ich habe viele Beispiele gesehen, die das verwenden.

+1

vielleicht keinen Unterschied machen, aber warum verwenden nicht [GetAsync] (https://msdn.microsoft.com/en-us/ Bibliothek/hh158944 (v = vs.118) .aspx) direkt? –

+0

Verwenden Sie OWIN-Pipeline mit Start? Möglicherweise müssen Sie 'TestServer' verwenden. https://blogs.msdn.microsoft.com/webdev/2013/11/26/unit-testing-owin-applications-using-testserver/ – Nkosi

+0

Ich benutze nicht owin. – duyn9uyen

Antwort

17

im folgenden Artikel referenzieren ich in der Lage war zu tun ...

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

indem sie mit einem HttpServer arbeiten und eine HttpConfiguration in HttpClient geben. Im folgenden Beispiel habe ich eine einfache ApiController erstellt, die Attribut-Routing verwendet. Ich konfigurierte das HttpConfiguration, um Attributrouten zuzuordnen, und übergab es dann an das neue HttpServer. Der HttpClient kann dann den konfigurierten Server verwenden, um Integrationstestanrufe an den Testserver zu senden.

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

Es war nicht notwendig, dass eine weitere Instanz von VS ausgeführt wurde, um den Controller zu testen.

Die folgende vereinfachte Version des Tests arbeitete auch

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

In Ihrem Fall Sie müssen sicherstellen, dass Sie den Server richtig konfigurieren Ihren Web-api-Setup anzupassen. Dies würde bedeuten, dass Sie Ihre API-Routen mit dem Objekt HttpConfiguration registrieren müssen.

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

Können Sie mir trotzdem eine Kopie dieser Lösung schicken? Ich habe den Code buchstäblich kopiert, aber es scheint nicht in meiner Lösung zu funktionieren. Also ich denke, dass einige Konfiguration anders ist. In den Eigenschaften meines Projekts wird das Projekt unter IIS Express unter einer Portnummer ausgeführt. Die Projekt-URL hat dieses "http: // localhost: 50892 /." Ich habe versucht, den Port und ohne die Port-Nummer in meinem Unit-Test, aber keinen Erfolg. Immer noch 404. – duyn9uyen

+0

Mit In-Memory-Test-Anfragen treffen die Anfragen nicht wirklich auf das Netzwerk, so dass es nichts mit IIS oder IIS Express zu tun hat. 'HttpServer' ein' DelegatingHandler', der die Anfrage behandelt, die der 'HttpClient' erzeugt. Es gibt keine Notwendigkeit für einen Port und der Standard ist "http: // localhost" wie in meinen Beispielen. Ich habe den genauen Code aus Ihrem Post genommen, nur den Host der URL geändert und den Controller so konfiguriert, dass er das Attribut-Routing verwendet. – Nkosi

+0

Kopieren Sie die genaue Einheitstestklasse wie in meiner Antwort und führen Sie sie in Ihrem Testprojekt aus. Es sollte laufen und passieren. Sie können dann überprüfen, wie es gemacht wurde und versuchen, die gleiche Technik auf Ihren tatsächlichen Test anzuwenden. Es gab nichts extra. Alles, was Sie dort sehen, ist genau so, wie es geschrieben und getestet wurde. – Nkosi

-3

Implementierung mit GET/POST-Tests habe ich -

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