2013-12-23 172 views
24

我正在使用以下方法通过ASP.NET Web API控制器上传文件。单元测试ASP.NET Web API控制器假冒HTTPContext

[System.Web.Http.HttpPost] 
public HttpResponseMessage UploadFile() 
{ 
    HttpResponseMessage response; 

    try 
    { 
     int id = 0; 
     int? qId = null; 
     if (int.TryParse(HttpContext.Current.Request.Form["id"], out id)) 
     { 
      qId = id; 
     } 

     var file = HttpContext.Current.Request.Files[0]; 

     int filePursuitId = bl.UploadFile(qId, file); 
    } 
    catch (Exception ex) 
    { 

    } 

    return response; 
} 

在我的单元测试,我调用UploadFile行动之前手动创建一个HTTPContext类:

var request = new HttpRequest("", "http://localhost", ""); 
var context = new HttpContext(request, new HttpResponse(new StringWriter())); 
HttpContext.Current = context; 

response = controller.UploadFile(); 

不幸的是,我没能自定义值添加到收藏Form,因为它是只读。此外,我无法更改Files集合。

是否有任何方法可以在单元测试期间向RequestFormFiles属性添加自定义值以添加所需的数据(标识和文件内容)?

回答

2

改为使用一些模拟框架,如Moq。用你需要的任何数据创建一个模拟HttpRequestBase和模拟HttpContextBase,并将它们设置在控制器上。

using Moq; 
using NUnit.Framework; 
using SharpTestsEx; 

namespace StackOverflowExample.Moq 
{ 
    public class MyController : Controller 
    { 
     public string UploadFile() 
     { 
      return Request.Form["id"]; 
     } 
    } 

    [TestFixture] 
    public class WebApiTests 
    { 
     [Test] 
     public void Should_return_form_data() 
     { 
      //arrange 
      var formData = new NameValueCollection {{"id", "test"}}; 
      var request = new Mock<HttpRequestBase>(); 
      request.SetupGet(r => r.Form).Returns(formData); 
      var context = new Mock<HttpContextBase>(); 
      context.SetupGet(c => c.Request).Returns(request.Object); 

      var myController = new MyController(); 
      myController.ControllerContext = new ControllerContext(context.Object, new RouteData(), myController); 

      //act 
      var result = myController.UploadFile(); 

      //assert 
      result.Should().Be.EqualTo(formData["id"]); 
     } 
    } 
} 
+18

谢谢您的答复。 由于我们使用web.api控制器,我们需要使用HttpControllerContext而不是ControllerContext。 HttpControllerContext不允许在构造函数HttpContextBase中传递。 –

+1

只是想知道为什么这是高调Pylyp Lebediev说是真实的... – rlee923

1

既然你有过这些类没有控制权,为什么不换行/抽象背后一个功能做控制

IRequestService request; 

[HttpPost] 
public HttpResponseMessage UploadFile() { 
    HttpResponseMessage response; 

    try { 
     int id = 0; 
     int? qId = null; 
     if (int.TryParse(request.GetFormValue("id"), out id)) { 
      qId = id; 
     } 

     var file = request.GetFile(0); 

     int filePursuitId = bl.UploadFile(qId, file); 
    } catch (Exception ex) { 
     //... 
    } 

    return response; 
} 

哪里request是你自定义的类型之一IRequestService

public interface IRequestService { 
    string GetFormValue(string key); 
    HttpPostedFileBase GetFile(int index); 
    //...other functionality you may need to abstract 
} 

并且可以像这样实施以将其注入到您的控制器中

public class RequestService : IRequestService { 

    public string GetFormValue(string key) { 
     return HttpContext.Current.Request.Form[key]; 
    } 

    public HttpPostedFileBase GetFile(int index) { 
     return new HttpPostedFileWrapper(HttpContext.Current.Request.Files[index]); 
    } 
} 

在你的单元测试

var requestMock = new Mock<IRequestService>(); 
//you then setup the mock to return your fake data 
//... 
//and then inject it into your controller 
var controller = new MyController(requestMock.Object); 
//Act 
response = controller.UploadFile();