Тестирование метода Web API, использующего HttpContext.Текущий.Запрос.Файлы?

я пытаюсь написать тест для метода веб-API, который использует HttpContext.Current.Request.Files и после исчерпывающего поиска и экспериментов я не могу понять, как издеваться над ним. Тестируемый метод выглядит следующим образом:

[HttpPost]
public HttpResponseMessage Post()
{
    var requestFiles = HttpContext.Current.Request.Files;
    var file = requestFiles.Get(0);
    //do some other stuff...
}

я понимаю, что есть другие вопросы похожее на это, но они не затрагивают эту конкретную ситуацию.

если я попытаюсь высмеять контекст, я столкнусь с проблемами с Http* объект иерархии. Скажем, я настраиваю различные макетные объекты (используя Moq) такой:

var mockFiles = new Mock<HttpFileCollectionBase>();
mockFiles.Setup(s => s.Count).Returns(1);
var mockFile = new Mock<HttpPostedFileBase>();
mockFile.Setup(s => s.InputStream).Returns(new MemoryStream());
mockFiles.Setup(s => s.Get(It.IsAny<int>())).Returns(mockFile.Object);
var mockRequest = new Mock<HttpRequestBase>();
mockRequest.Setup(s => s.Files).Returns(mockFiles.Object);
var mockContext = new Mock<HttpContextBase>();
mockContext.Setup(s => s.Request).Returns(mockRequest.Object);

попытка присвоить его текущему контексту...

HttpContext.Current = mockContext.Object;

...приводит к ошибке компилятора / redline, потому что это Cannot convert source type 'System.Web.HttpContextBase' to target type 'System.Web.HttpContext'.

я также попытался сверлить различные объекты контекста, которые поставляются с построенным объектом контроллера, но не могу найти тот, который a) является возвращаемым объектом HttpContext.Current вызов в теле метода контроллера и b) обеспечивает доступ к стандарт HttpRequest свойства, как Files.

var requestMsg = controller.Request;   //returns HttpRequestMessage
var context = controller.ControllerContext;  //returns HttpControllerContext
var requestContext = controller.RequestContext;   //read-only returns HttpRequestContext

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

есть ли способ, чтобы поглумиться HttpContext.Current.Request.Files для модульного тестирования в Web API?

обновление
Хотя я не уверен, что это будет принято командой, я экспериментирую с изменением метода Post на использовать Request.Content, как полагает Мартин Liversage. В настоящее время это выглядит примерно так:

public async Task<HttpResponseMessage> Post()
{
    var uploadFileStream = new MultipartFormDataStreamProvider(@"C:temp");
    await Request.Content.ReadAsMultipartAsync(uploadFileStream);
    //do the stuff to get the file
    return ActionContext.Request.CreateResponse(HttpStatusCode.OK, "it worked!");
}

мой тест выглядит примерно так:

var byteContent = new byte[]{};
var content = new MultipartContent { new ByteArrayContent(byteContent) };
content.Headers.Add("Content-Disposition", "form-data");
var controllerContext = new HttpControllerContext 
{
    Request = new HttpRequestMessage
        {
            Content = new MultipartContent { new ByteArrayContent(byteContent) }
        }
};

теперь я получаю ошибку на ReadAsMultipartAsync:

System.IO.IOException: Error writing MIME multipart body part to output stream. ---> System.InvalidOperationException: The stream provider of type 'MultipartFormDataStreamProvider' threw an exception. ---> System.InvalidOperationException: Did not find required 'Content-Disposition' header field in MIME multipart body part.

2 ответов


Web API был построен для поддержки модульного тестирования, позволяя вам издеваться над различными объектами контекста. Однако, используя HttpContext.Current вы используете "старые" System.Web код, который использует HttpContext класс, который делает невозможным модульное тестирование кода.

чтобы ваш код был тестируемым, вы должны прекратить использовать HttpContext.Current. В отправка данных HTML-формы в ASP.NET Web API: загрузка файлов и Multipart MIME вы можете увидеть, как загружать файлы с помощью веб-API. Иронически, этот код также использует HttpContext.Current чтобы получить доступ к MapPath но в Web API вы должны использовать HostingEnvironment.MapPath это также работает вне IIS. Издевательство над более поздним также проблематично, но пока я фокусируюсь на вашем вопросе о издевательстве над просьбой.

не используя HttpContext.Current позволяет вам модульный тест вашего контроллера, назначив ControllerContext свойства контроллера:

var content = new ByteArrayContent( /* bytes in the file */ );
content.Headers.Add("Content-Disposition", "form-data");
var controllerContext = new HttpControllerContext {
  Request = new HttpRequestMessage {
    Content = new MultipartContent { content }
  }
};
var controller = new MyController();
controller.ControllerContext = controllerContext;

общепринятого ответа на вопрос ОП. Я хотел добавить свое решение здесь, которое происходит от Мартина, так как это страница, на которую я был направлен, когда просто искал, как издеваться над объектом запроса для веб-API, чтобы я мог добавлять заголовки, которые ищет мой контроллер. Мне было трудно найти простой ответ:--2-->

   var controllerContext = new HttpControllerContext();
   controllerContext.Request = new HttpRequestMessage();
   controllerContext.Request.Headers.Add("Accept", "application/xml");

   MyController controller = new MyController(MockRepository);
   controller.ControllerContext = controllerContext;

и вот вы; очень простой способ создать контекст контроллера, с помощью которого вы можете "макетировать" объект запроса и предоставить правильные заголовки для метода контроллера.