I was trying to unit test a method in one of my Controllers returning a JsonResult. To my surprise the following code didn't work:
[HttpPost]
public JsonResult Test() {
return Json(new {Id = 123});
}
This is how I test it (also note that the test code resides in another assembly):
// Act
dynamic jsonResult = testController.Test().Data;
// Assert
Assert.AreEqual(123, jsonResult.Id);
The Assert throws an exception:
'object' does not contain a definition for 'Id'
I've since resolved it by using the following:
[HttpPost]
public JsonResult Test() {
dynamic data = new ExpandoObject();
data.Id = 123;
return Json(data);
}
I'm trying to understand why isn't the first one working ? It also seems to be working with basically anything BUT an anonymous type.
To be clear, the specific problem you are encountering is that C# dynamic does not work with non-public members. This is by design, presumably to discourage that sort of thing. Since as LukLed stated, anonymous types are public only within the same assembly (or to be more precise, anonymous types are simply marked internal, not public), you are running into this barrier.
Probably the cleanest solution would be for you to use InternalsVisibleTo. It allows you to name another assembly that can access its non-public members. Using it for tests is one of the primary reasons for its existance. In your example, you would place in your primary project's AssemblyInfo.cs the following line:
[assembly: InternalsVisibleTo("AssemblyNameOfYourTestProject")]
Once you do that, the error will go away (I just tried it myself).
Alternatively, you could have just used brute force reflection:
Assert.AreEqual(123, jsonResult.GetType().GetProperty("Id").GetValue(jsonResult, null));
Having read the responses here and then looking further afield I found a 2009 msdn blog post with a different approach again. But.. in the comments was a very simple and very elegant solution by Kieran ... to use .ToString().
In your original case:
[HttpPost]
public JsonResult Test()
{
return Json(new {Id = 123});
}
You could test by doing:
var jsonResult = controller.Test();
Assert.AreEqual("{Id = 123}", jsonResult.Data.ToString());
I much prefer this solution as it:
avoids changing the original code (InternalsVisibleTo, ExpandoObject),
avoids using MvcContrib and RhinoMocks (no issue with either of these but why add just to be able to test JsonResult?), and,
avoids using Reflection (adds complexity to the tests).
Anonymous types are internal, so you can't expose them to another library, the one with tests. If you placed testing code in the same library as controller, it will work.
Related
I'm using a generic repository in my MVC application and I also use automapper for mapping between the viewmodel. The reason I need this is to avoid circular references from my models and also to leverage data annotations etc. I have recenlt swithced to a Generic Repository pattern and unit of work but I no longer seem to be able to use Automapper's .ProjectTo.
Here is my code, this is called by a KendoUI MVC Grid, it returns a list of data (which has a relationship and thus circular references).
public ActionResult GetItems([DataSourceRequest] DataSourceRequest request)
{
var item = unitOfWork.ItemRepository.Get();
var result = item.ToDataSourceResult(request);
return Json(result);
}
To get around the circular reference I use a viewmodel, this also allows me to have additional control over my data. What I find is that it won't let me use project to.
public ActionResult GetItems([DataSourceRequest] DataSourceRequest request)
{
var item = unitOfWork.ItemRepository.Get().ProjectTo<ItemViewModel>;
var result = item.ToDataSourceResult(request);
return Json(result, JsonRequestBehavior.AllowGet);
}
Intellisense returns an error of:
Cannot assign method group to an implicitly-typed variable
What is going wrong and is there another way I can do this?
I think you're just missing parenthesis off the end of the ProjectTo<>() call:
var item = unitOfWork.ItemRepository.Get().ProjectTo<ItemViewModel>();
I'm creating a custom ModelBinder in .NET Core 1.1.0, and I think I have it mostly figured out. I want to use this binder only when I specifically want to, but I can't figure out how to accomplish this. I want this model binder to be ignored when I don't call it, but when I call it I want all others to be ignored. How can I accomplish this?
The two things that seem feasible are the parameter attributes [Bind] and [ModelBinder], but neither of these really works.
I first tried the below:
[HttpGet]
public async Task<IActionResult> Get([Bind("test")] int userId)
{
// stuff
}
When my custom IModelBinderProvider is hit, the ModelBinderProviderContext contains a BindingInfo property, which in turn contains an IPropertyFilterProvider. When debugging, that filter provider contains a collection called Include with my value of test. There doesn't appear to be any way to check for that programmatically, however--there's no way that I can find to actually access that collection. It's null if nothing is set, so I could hypothetically check for null, but that's very messy and isn't a good idea.
To illustrate, here's the debugger info for the ModelBinderProviderContext:
Next, I tried using this code:
[HttpGet]
public async Task<IActionResult> Get(
[ModelBinder(BinderType = typeof(MyModelBinder))] int userId
)
{
// stuff
}
This attribute appears to have no effect whatsoever. It does not force MyModelBinder to be used; model binders are used in the order specified in Startup.cs (the list in MvcOptions).
The PropertyFilterProvider contains a BindAttribute instance which implements the IPropertyFilterProvider interface. You could cast the instance to a BindAttribute and access the Include property:
var bindAttribute = context.BindingInfo.PropertyFilterProvider as BindAttribute;
var include = bindAttribute?.Include;
Keep in mind that the cast may not succeed, resulting in bindingAttribute being null.
I'm trying to write a convention test that specifies that a method should only be called in some contexts - specifically I have a static Empty getter that I only want to allow used in test methods, vis methods decorated with TestAttribute.
I know that I should also mark the getter as obsolete, use another method etc, but I also want a convention test around this so it doesn't break in the future.
I am guessing I want to use static analysis through reflection in my convention test. How would I go about performing this kind of analysis?
Yes, Roslyn can help with this sort of thing. An example of what this might look like as a standalone analysis would be something like:
var solution = Solution.Load(pathToSolution);
foreach (var project in solution.Projects)
{
var type = project.GetCompilation().GetTypeByMetadataName(typeNameContainingMethod);
var method = type.GetMembers("Empty").Single();
var references = method.FindAllReferences(solution);
foreach (var referencedSymbol in references)
{
foreach (var referenceLocation in references)
{
CheckIfCallIsAllowed(referenceLocation);
}
}
}
You might also look at the Creating a Code Issue walkthrough and the Code Issue template that comes with the Roslyn CTP for another approach to doing this at edit time, instead of in a test.
An action method in my ASP.NET MVC2 application returns a JsonResult object and in my unit test I would like to check that the returned JSON object indeed contains the expected values.
I tried this:
1. dynamic json = ((JsonResult)myActionResult).Data;
2. Assert.AreEqual(JsonMessagesHelper.ErrorLevel.ERROR.ToString(), json.ErrorLevel);
But I get a RuntimeBinderException "'object' does not contain a definition for 'ErrorLevel'".
However, when I place a breakpoint on line 2 and inspect the json dynamic variable (see picture below), it obviously does contain the ErrorLevel string and it has the expected value, so if the runtime binder wasn't playing funny the test would pass.
What am I not getting? What am I doing wrong and how can I fix this? How can I make the assertion pass?
You don't really need dynamic. Here's an example. Suppose you had the following action which you would like to unit test:
public ActionResult Index()
{
return Json(new { Id = 5, Foo = "bar" });
}
and the corresponding test:
// act
var actual = subjectUnderTest.Index();
// assert
var data = new RouteValueDictionary(actual.Data);
Assert.AreEqual(5, data["Id"]);
Assert.AreEqual("bar", data["Foo"]);
Also you might find the following blog post useful.
The Data property of the JsonResult is of type Object this means, although you have a dynamic declaration, the type that is set is still Object. The other issue is that you are using an anonymous type as the Data and then trying to access that as a declared instance outside of its applicable scope. Use #Darin's technique for accessing the property values using a RouteValueDictionary.
I've been using T4MVC (FYI: v2.6.62) for quite some time, and I've been slowly moving over our code to this way of working (less reliance on magic strings).
But I've had to stop because, for some reason, T4MVC is unable to translate objects into urls, and only seems to be able to work on primitive types (int/string/etc).
Here is an example:
Route breakdown:
/MyController/MyAction/{Number}/{SomeText}
Class:
namespace MyNamespace
{
public class MyClass
{
public int Number { get; set; }
public string SomeText { get; set; }
}
}
Controller:
public class MyController
{
public virtual ActionResult MyAction(MyClass myClass)
{
return View();
}
}
View:
<%= Html.Action(
T4MVC.MyController.Actions.MyAction(
new MyClass()
{
Number = 1,
SomeText = "ABC"
}
) %>
The end result is this:
/MyController/MyAction?myClass=MyNamespace.MyClass
and not
/MyController/MyAction/1/ABC
Does anyone else have this problem? Are T4MVC urls like this available?
Question also asked at the ASP.NET Forum.
Update (10/11/2012): the recently added support for Model Unbinders (see section 3.1 in the doc) should hopefully cover a lot of these cases.
Original answer:
Copying my reply from the forum thread:
Hmmm, I don't think this has come up yet. Maybe in most cases that people have Action methods that take an object, the object's values come from posted form data, rather than being passed on the URL? In such scenario, the question doesn't arise.
I think in theory T4MVC could be changed to support this. It would just need to promote all the object's top level properties as route values rather than try to use the object itself (obviously, the current behavior is bogus, and is a result of just calling ToString() blindly).
Have others run into this and think it's worth addressing?
If I've understood the problem correctly then the following syntax should allow you to work around the problem.
<%= Html.ActionLink("test", MVC.MyController.MyAction().AddRouteValues(new MyClass() { Number = 5, SomeText = "Hello" })) %>
I think the answer to make the syntax nicer would be to wrap each non value type parameter in a RouteValueDictionary in each generated action result method
Edit: (Response to comment as not enough chars)
Ah ok I managed to recreate the simple example above using this method to give: /MyController/MyAction/5/Hello as the url.
I'm not quite sure how nested complex types would pan out in practice. You could use some recursion to dive down the into the top-level object and reflect over the values to add them but then you open up a new set of issues, such as how to cope with a child property name that is identical to the parent property name.
This seems like it could be a complex problem to solve, in a manner that would work for everyone.
Perhaps some kind of adapter pattern would be most useful to transform a complex object into route values. In the simplest case this might be to declare an extension method ToRouteDictionary that acts on your complex type and transforms it using your knowledge of how it should work. Just thinking out loud as I'm obviously not aware of your use cases