如何在单元测试中创建JSON POST以请求MVC控制器

6

如果您使用的是一个期望JSON POST的控制器方法,例如...

public class MyController : Controller
{
    [HttpPost]
    public ActionResult PostAction()
    {
        string json = new StreamReader(Request.InputStream).ReadToEnd();
        //do something with json
    }
}

当你尝试测试控制器时,如何设置单元测试以将POST数据发送到控制器?

2个回答

6

要传递数据,您可以使用模拟的http上下文设置控制器上下文,并传递一个虚假的请求正文流。

使用moq来伪造请求。

[TestClass]
public class MyControllerTests {
    [TestMethod]
    public void PostAction_Should_Receive_Json_Data() {
        //Arrange

        //create a fake stream of data to represent request body
        var json = "{ \"Key\": \"Value\"}";
        var bytes = System.Text.Encoding.UTF8.GetBytes(json.ToCharArray());
        var stream = new MemoryStream(bytes);

        //create a fake http context to represent the request
        var mockHttpContext = new Mock<HttpContextBase>();
        mockHttpContext.Setup(m => m.Request.InputStream).Returns(stream);

        var sut = new MyController();
        //Set the controller context to simulate what the framework populates during a request
        sut.ControllerContext = new ControllerContext {
            Controller = sut,
            HttpContext = mockHttpContext.Object
        };

        //Act
        var result = sut.PostAction() as ViewResult;

        //Assert
        Assert.AreEqual(json, result.Model);
    }

    public class MyController : Controller {
        [HttpPost]
        public ActionResult PostAction() {
            string json = new StreamReader(Request.InputStream).ReadToEnd();
            //do something with json
            //returning json as model just to prove it received the data
            return View((object)json);
        }
    }
}

首先,一些建议:

不要重复造轮子。

MVC框架已经提供了解释发送到控制器动作(横切关注点)的数据的功能。这样,您就不必担心必须自己创建模型来使用。框架会为您完成此操作。它将使您的控制器动作更加清晰、易于管理和维护。

如果可能的话,应考虑向您的动作发送强类型数据。

public class MyController : Controller {
    [HttpPost]
    public ActionResult PostAction(MyModel model) {
        //do something with model
    }
}

这个框架基本上会通过使用它的 ModelBinder 进行参数绑定,从而自动执行您在操作中手动执行的操作。如果匹配,它将反序列化请求正文并将传入数据的属性绑定到操作参数。

此外,它还可以更轻松地对控制器进行单元测试。

[TestClass]
public class MyControllerTests {
    [TestMethod]
    public void PostAction_Should_Receive_Json_Data() {
        //Arrange

        var model = new MyModel { 
          Key = "Value"
        };

        var sut = new MyController();

        //Act
        var result = sut.PostAction(model);

        //Assert
        //...assertions here.
    }
}

非常感谢。我不确定如何发送json。至于重新发明轮子,我完全同意你的看法。然而,在这种情况下,我正在编写一个MVC服务,将接受来自第三方的JSON,而不是来自同一MVC应用程序的视图。由于JSON来自其他来源,我可能需要进行额外的解析,以从不同命名的属性中加载模型。再次感谢。 - geoff swartz
那么这更像是 ASP.Net Web API 的工作,而不是纯 MVC。你应该研究一下它。 - Nkosi
提醒不要重复造轮子的话让我省了些时间,它让我想起了我实际上要测试什么。谢谢! - bkwdesign
耶!!我已经寻找了很久关于如何在MVC中模拟HttpContext的信息。谢谢! - Scott Fraley

0

对于 HttpGetHttpPost,您可以以相同的方式对控制器操作执行单元测试。

[Test]
public void Test_MyController_PostAction()
{
    var controller = new MyController();
    ActionResult result = controller.PostAction();

    // Assert here
}

假设您的操作需要一个模型参数。

[Test]
public void Test_MyController_PostAction()
{
    var controller = new MyController();
    ActionResult result = controller.PostAction(new SomeModel()); // Just pass as parameter

    // Assert here
}

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