5

So I have a controller that returns json to my views that I need to test. I have tried using reflection with a dynamic data type to access a sub property of a list but I keep getting something similar to 'unable to cast' errors. Basically I have a list within a list that I want to access and verify things about but I can't access it. Has anyone tested json returned from their controller before in MVC4 and have advice?

Code:

// arrange
        var repositoryMock = new Mock<IEdwConsoleRepository>();
        var date = -1;
        var fromDate = DateTime.Today.AddDays(date);
        EtlTableHistory[] tableHistories =
            {
                new EtlTableHistory
                    {
                        Table = new Table(),
                        RelatedStatus = new BatchStatus(),
                        BatchId = 1
                    }
            };
        EtlBatchHistory[] batchHistories = { new EtlBatchHistory
                                             {
                                                 Status = new BatchStatus(),
                                                 TableHistories = tableHistories
                                             } };

        repositoryMock.Setup(repository => repository.GetBatchHistories(fromDate)).Returns((IEnumerable<EtlBatchHistory>)batchHistories);
        var controller = new EdwController(new Mock<IEdwSecurityService>().Object, repositoryMock.Object);

        // act
        ActionResult result = controller.BatchHistories(1);

        // assert
        Assert.IsInstanceOfType(result, typeof(JsonResult), "Result type was incorrect");
        var jsonResult = (JsonResult)result;
        var resultData = (dynamic)jsonResult.Data;
        var returnedHistories = resultData.GetType().GetProperty("batchHistories").GetValue(resultData, null);

        var returnedTableHistoriesType = returnedHistories.GetType();
        Assert.AreEqual(1, returnedTableHistoriesType.GetProperty("Count").GetValue(returnedHistories, null), "Wrong number of logs in result data");
6
  • I want to get the 'TableHistories' property out of the EtlBatchHistory object within batchHistories. Commented Sep 26, 2013 at 14:56
  • What does your JSON look like, and/or what code produces it? And you should be able to just use var returnedHistories = resultData.batchHistories;, without reflection (that's what dynamic is for). (same for the Count property) Commented Sep 26, 2013 at 15:03
  • Json looks like: "{ batchHistories = System.Collections.Generic.List1[<>f__AnonymousType111[System.Int32,System.String,System.String,System.String,System.Int32,System.Nullable1[System.Int64],System.Nullable1[System.Int32],System.String,System.String,System.String,System.Collections.Generic.List1[<>f__AnonymousType09[System.String,System.String,System.Int32,System.Int32,System.String,System.String,System.String,System.String,System.String]]]] }" Commented Sep 26, 2013 at 15:14
  • I get a 'object' does not contain a definition for 'batchHistories' error when I try to access batchHistories directly off of resultData. Commented Sep 26, 2013 at 15:19
  • Your JSON is not a good serialization of data. It looks like you wrote it by doing a ToString() on a List<T>. Fix that. Commented Sep 26, 2013 at 15:22

2 Answers 2

3

Here's an example:

Controller:

    [HttpPost]
    public JsonResult AddNewImage(string buildingID, string desc)
    {
        ReturnArgs r = new ReturnArgs();

        if (Request.Files.Count == 0)
        {
            r.Status = 102;
            r.Message = "Oops! That image did not seem to make it!";
            return Json(r);
        }
        if (!repo.BuildingExists(buildingID))
        {
            r.Status = 101;
            r.Message = "Oops! That building can't be found!";
            return Json(r);
        }

        SaveImages(buildingID, desc);
        r.Status = 200;
        r.Message = repo.GetBuildingByID(buildingID).images.Last().ImageID;

        return Json(r);
    }

    public class ReturnArgs
    {
        public int Status { get; set; }
        public string Message { get; set; }
    }

Test:

    [TestMethod]
    public void AddNewImage_Returns_Error_On_No_File()
    {
        // Arrange
        ExtendedBuilding bld = repo.GetBuildings()[0];
        string ID = bld.Id;

        var fakeContext = new Mock<HttpContextBase>();
        var fakeRequest = new Mock<HttpRequestBase>();

        fakeContext.Setup(cont => cont.Request).Returns(fakeRequest.Object);
        fakeRequest.Setup(req => req.Files.Count).Returns(0);

        BuildingController noFileController = new BuildingController(repo);
        noFileController.ControllerContext = new ControllerContext(fakeContext.Object, new System.Web.Routing.RouteData(), noFileController);

        // Act
        var result = noFileController.AddNewImage(ID, "empty");

        ReturnArgs data = (ReturnArgs)(result as JsonResult).Data;

        // Assert
        Assert.IsTrue(data.Status == 102);
    }

In your example it looks to me that the problem is here:

var resultData = (dynamic)jsonResult.Data;
var returnedHistories = resultData.GetType().GetProperty("batchHistories").GetValue(resultData, null);

The resultData object would be the exact type of the object that you returned in your action. So if you did something like:

List<String> list = repo.GetList();
return Json(list);

Then your resultData would be of type:

List<String>

Try making sure that you are returning your Object using the Json(obj) function.

Sign up to request clarification or add additional context in comments.

2 Comments

thanks for the post! i am currently using the Json(obj) function, from my controller i return a ActionResult object which is a System.Web.Mvc.JsonResult (might be wrong on that namespacing but its something close to that), after using reflection I get the batchHistories property out of that which is an anonymous list of BatchHistory objects. within that is another anonymous list, but i can't access either with reflection and i can deserialize it for some reason..
No problem. Have you tried strongly typing your return object and then unboxing jsonResult.Data as that type in your test? On another note, not sure why the reflection isn't working!
0

You can deserialize your Json into a dynamic object and then ask for the property you want

Example:

dynamic obj = Newtonsoft.Json.JsonConvert.DeserializeObject(json);

var propertyValue = obj.MyProperty; //Ask for the right property

You can add Json serializer from Nuget Json.Net package.

4 Comments

i can't use newtonsoft on a actionresult though, which is what my controller is returning. i guess i should have specified better.
Ok, I think I understand your post now. You may have something wrong in the json, what is the content of jsonResult.Data? You will get the json string doing JsonConvert.SerializeObject(jsonResult.Data)
check my comment on the original post for what the json looks like, thanks for your help!
That doesn't seem to be a valid Json, I guess you may have some issue in controller.BatchHistories method.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.