Testing a Web API method that uses HttpContext.Current.Request.Files?
我试图编写一个使用
1 2 3 4 5 6 7 | [HttpPost] public HttpResponseMessage Post() { var requestFiles = HttpContext.Current.Request.Files; var file = requestFiles.Get(0); //do some other stuff... } |
我意识到还有其他与此类似的问题,但是它们并不能解决这种特定情况。
如果尝试模拟上下文,则会遇到
1 2 3 4 5 6 7 8 9 | 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); |
尝试将其分配给当前上下文...
1 | HttpContext.Current = mockContext.Object; |
...导致编译器错误/红线,因为它是
我还尝试了对构造的控制器对象附带的各种上下文对象进行钻取,但是找不到一个是a)是控制器方法主体中
1 2 3 | var requestMsg = controller.Request; //returns HttpRequestMessage var context = controller.ControllerContext; //returns HttpControllerContext var requestContext = controller.RequestContext; //read-only returns HttpRequestContext |
还需要注意的一点是,我根本无法更改正在测试的控制器,因此无法更改构造函数以允许注入上下文。
有什么方法可以模拟
更新资料
尽管我不确定这是否会被团队接受,但我正在尝试按照Martin Liversage的建议将Post方法更改为使用
1 2 3 4 5 6 7 | 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!"); } |
我的测试看起来与此类似:
1 2 3 4 5 6 7 8 9 10 | 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) } } }; |
现在我在
通过允许您模拟各种上下文对象,构建了Web API以支持单元测试。但是,通过使用
要使代码可进行单元测试,必须停止使用
不使用
1 2 3 4 5 6 7 8 9 | 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; |
接受的答案非常适合OP的问题。我想在此处添加源自Martin的解决方案,因为这是我在仅搜索如何模拟出Web API的Request对象时可以直接转到的页面,因此我可以添加Controller寻找的标头。我很难找到简单的答案:
1 2 3 4 5 6 |
你在那里;这是一种创建控制器上下文的非常简单的方法,您可以通过该方法"模拟"出Request对象,并为Controller方法提供正确的标头。
我只是嘲笑发布的文件。我相信所有文件也可以通过这种方式来模拟。
This was in my controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | private HttpPostedFileBase _postedFile; /// <summary> /// For mocking HttpPostedFile /// </summary> public HttpPostedFileBase PostedFile { get { if (_postedFile != null) return _postedFile; if (HttpContext.Current == null) { throw new InvalidOperationException("HttpContext not available"); } return new HttpContextWrapper(HttpContext.Current).Request.Files[0]; } set { _postedFile = value; } } [HttpPost] public MyResponse Upload() { if (!ValidateInput(this.PostedFile)) { return new MyResponse { Status ="Input validation failed" }; } } private bool ValidateInput(HttpPostedFileBase file) { if (file.ContentLength == 0) return false; if (file.ContentType !="test") return false; if (file.ContentLength > (1024 * 2048)) return false; return true } |
This was in my Unit test case
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | public void Test1() { var controller = new Mock<MyContoller>(); controller.Setup(x => x.Upload).Returns(new CustomResponse()); controller.Request = new HttpRequestMessage(); controller.Request.Content = new StreamContent(GetContent()); controller.PostedFile = GetPostedFile(); var result = controller.Upload().Result; } private HttpPostedFileBase GetPostedFile() { var postedFile = new Mock<HttpPostedFileBase>(); using (var stream = new MemoryStream()) using (var bmp = new Bitmap(1, 1)) { var graphics = Graphics.FromImage(bmp); graphics.FillRectangle(Brushes.Black, 0, 0, 1, 1); bmp.Save(stream, ImageFormat.Jpeg); postedFile.Setup(pf => pf.InputStream).Returns(stream); postedFile.Setup(pf => pf.ContentLength).Returns(1024); postedFile.Setup(pf => pf.ContentType).Returns("bmp"); } return postedFile.Object; } |
Although, I was not able to successfully mock the HTTPContext. But, I
was able to mock the file upload.