使用假的HTTPContext对ASP.NET Web API控制器进行单元测试

26

我正在使用以下方法通过ASP.NET Web API控制器上传文件。

[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属性添加所需数据(id和文件内容)?

2个回答

1
使用一些模拟框架,例如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"]);
        }
    }
}

22
谢谢您的回复。 由于我们使用web.api控制器,因此需要使用HttpControllerContext而不是ControllerContext。 HttpControllerContext不允许在构造函数中传递HttpContextBase。 - Pylyp Lebediev
3
只是想知道为什么Pylyp Lebediev所说的是真的,但这个帖子却被点赞了。 - rlee923

0

既然您无法控制那些类,为什么不将功能封装/抽象到一个可控的类中呢?

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接