简体   繁体   中英

how to create json post in unit test to mvc controller

With a controller with a method that is expecting a json post such as...

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

how do you set up a unit test to send the post data to the controller when you're trying to test it?

To pass the data you can set the controller context with a mocked http context and pass a fake stream of the request body.

Used moq to fake the request.

[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);
        }
    }
}

With that out of the way, now some advice.

Don't reinvent the wheel.

The MVC framework already provides the functionality for interpreting data sent to controller action (Cross-cutting concerns). That way you don't have to worry yourself with having to hydrate a model to work with. The framework will do it for you. It will make your controller actions cleaner and easier to manage and maintain.

You should consider sending strongly typed data to your actions if possible.

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

The framework will basically do exactly what you are doing manually in your action by doing what is called parameter binding using its ModelBinder. It will deserialize the body of the request and bind properties of the incoming data to the action parameter if they match.

With that it also allows for easier unit testing of your controllers

[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.
    }
}

You perform unit test on the controller actions the same way for both HttpGet and HttpPost .

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

    // Assert here
}

Let's say your action expects a model parameter.

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

    // Assert here
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM