Тестирование/интеграция Тестирование веб-API с помощью HttpClient в Visual Studio 2013

Мне нелегко пытаться протестировать мой контроллер API с помощью Visual Studio 2013. Мое одно решение имеет проект веб-API и проект тестирования. В моем тестовом проекте у меня есть Unit Test с этим:

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

Я продолжаю получать 404. Я попытался запустить свой API с одним экземпляром Visual Studio (IIS Express) и попытаться отладить этот Unit Test в другом экземпляре. Но не повезло. Я проверил, что могу поместить этот URL в браузер (когда одна Visual Studio отлаживается), и я вижу ответ JSON. Но я не могу понять, как заставить его работать с моими Unit Test и HttpClient. Я пытался найти примеры в Интернете, но, похоже, не нашел их. Может кто-нибудь помочь?

ОБНОВЛЕНИЕ 1: Я попытался добавить маршрут, но ничего не произошло.

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]

Вот мой API-контроллер

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

ОБНОВЛЕНИЕ Разрешение: Я смог заставить его работать, если я обновляю HttpClient без объекта HttpServer. Тем не менее, мне все равно нужно иметь два экземпляра VS. 1 запустил мой код API, а другой - для запуска Unit Test.

Вот рабочий метод.

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

Кто-нибудь знает, почему он не работает с HttpServer и a HttpConfiguration, переданными в HttpClient? Я видел много примеров, которые используют это.

Ответ 1

Ссылка на следующую статью, которую я смог сделать...

Тестирование интеграции ASP.NET Web API с хостингом в памяти

работая с HttpServer и a HttpConfiguration, переданными в HttpClient. В следующем примере я создал простой ApiController, который использует маршрутизацию атрибутов. Я настроил маршрут HttpConfiguration для сопоставления атрибутов, а затем передал его новому HttpServer. Затем HttpClient может использовать настроенный сервер для выполнения тестовых вызовов интеграции на тестовом сервере.

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

Не нужно было использовать другой экземпляр VS для интеграции теста контроллера.

Кроме того, была проведена следующая упрощенная версия теста

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

В вашем случае вам нужно убедиться, что вы правильно настроили сервер, чтобы он соответствовал вашей настройке api. Это означало бы, что вам необходимо зарегистрировать маршруты api с помощью объекта HttpConfiguration.

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

Ответ 2

Выполнение с использованием тестов GET/POST, которые я использовал -

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