Blog
Using dynamic to unit test an Action Method that returns JsonResult with MVC 3 and C# 4
We’ve all been there, haven’t we? We have an action method that we call through Ajax.ActionLink, Ajax.BeginForm or even with jQuery.ajax, and returning a JsonResult is what makes the most sense. But whatever you pass to a JsonResult only gets exposed through the JsonResult.Data property which, unfortunately, is of type object.
JsonResult is meant to be serialized into a string with Json notation so it doesn’t matter that Data is of type object, but, how do you access the properties inside the Data object when you want to unit test your view?
The setup
We need an action in a controller that returns a JsonResult; it doesn’t matter how it gets called from the view or what we’d do with the result, so let’s keep it simple:
public ActionResult DoSomething()
{
var ok = Act();
var message = ok? "All done!" : "Oops!";
return Json(new { Message = message });
}
And the unit test:
[TestClass]
public class Test
{
[TestMethod]
public void DoSomething_Does_Something()
{
// Arrange
var controller = TestFakes.CreateController();
// Act
var json = controller.DoSomething() as JsonResult;
// Assert
Assert.IsNotNull(json.Data.Message); // --> FAIL!
}
}
The test fails not because Message is null, but because json.Data is of type System.Object and System.Object doesn’t have a Message property, obviously.
What now? Well, I give it away in the title of this post so it’s hardly a cliff-hanger, is it?
Dynamic to the rescue
Thanks to the magic of statically typing as dynamic in c# 4 this should be a piece of cake. Let’s modify the test method:
[TestClass]
public class Test
{
[TestMethod]
public void DoSomething_Does_Something()
{
// Arrange
var controller = TestFakes.CreateController();
// Act
var json = controller.DoSomething() as JsonResult;
// Dynamic magic: just assign the Data property
// to a variable of type dynamic!
dynamic data = json.Data;
// And get our message out of it.
string msg = data.Message;
// Assert
Assert.AreEqual("All done!", msg); // --> OK!
}
}
It’s that simple: just assign the Data object to a variable of type dynamic, then access the Message property. Note that I cannot declare the variable msg as var. That’s because data.Message is a dynamic expression that will be resolved at run time, so the compiler wouldn’t know what to substitute var for at compile time.
Er, it doesn’t work
If you actually run this test, you’ll get an "object does not contain a definition for Message" error. Oops. Didn’t we convert json.Data to a dynamic object? Well, yes but…
Anonymous types are internal
JsonResult returns an anonymous object and these are, by default, internal, so they need to be made visible to the tests project.
The solution
This is, fortunately, very very simple to solve.
Assuming you have your test classes in an assembly called MyProject.Tests, find the file AssemblyInfo.cs in the Properties folder in your MVC project. Open AssemblyInfo.cs and add the following line to the end of the file:
[assembly: InternalsVisibleTo("MyProject.Tests")]
Do you know of a better way? Any questions? Let me know!
11 comment(s)
@David - Glad I can help! :)
I was having trouble getting this to work and I actually started searching on StackOverflow (to no avail) and Google. After a while a Google search brought up the "anonymous types are internal" issue. Bit of luck there I guess :)
Thanks. Thats really what I wanted!
@Andrey - You're most welcome =)
Thank you very much! I think the last paragraph should read "find the file AssemblyInfo.cs in the Properties folder in the project to test" rather than "find the file AssemblyInfo.cs in the Properties folder in your tests project" however (wrong way round I think!) - but thank you this was extremely helpful.
@Adam,
Thanks, you're kinda right: it was supposed to say "in your main project". I've fixed it now, thanks again!
Sergi,
Thanks for explanation. Very interesting.
@Eduardo,
You're most welcome :)
Very nice solution, Sergei! thanks for Sharing, i was about to put in an ugly hack when i ran across your post! You rock.
Thanks a lot for this post! In my case I was passing dynamic objects FROM the MyProject.Test to the MyProject cause a class in MyProject was working with dynamic objects which I was mocking with Moq, so I had to put InternalsVisibleTo("MyProject.Test") into the MyProject AssemblyInfo.cs (so I could test internals of MyProject in MyProject.Test) AND ALSO put InternalsVisibleTo("MyProject") in MyProject.Test AssemblyInfo.cs so the code in MyProject could find some properties on the dynamic anonymous object :)
this post got me on the right when I realized that anonymous objects are internal, thanks again!!
@FT, @Siggi,
You're welcome; I'm glad I could help.
And thank you for your comments! ;)

That is going to save me a lot of headaches! Thanks for that. Question is, how did you find out about the solution to the problems you've encountered?