AnthonySteele / MvcRouteTester

A library for unit testing ASP MVC route tables for both Web and API Routes
Apache License 2.0
105 stars 43 forks source link

Error when controller param has object with a property that has the same name as a route key #21

Closed josefresno closed 10 years ago

josefresno commented 11 years ago

//Class public class Item { public int Id { get; set; } }

//Route config.Routes.MapHttpRoute("Create Item", "items/{id}", new { controller = "Item", action = "CreateItem" }, new { httpMethod = new HttpMethodConstraint(HttpMethod.Post) });

//Controller [HttpPost] public void CreateItem(string id, [FromBody] Item item) { }

//Test1 [Test] public void CreateItem_Exists1() {

            var config  = new HttpConfiguration();
            WebApiConfig.Register(HttpConfiguration); 
            var item = new Item();
           config
               .ShouldMap(string.Format("http://localhost/items/aid"))

                .WithBody(HttpUtility.UrlEncode(Json.Encode(item)))
                .To<ItemController>(HttpMethod.Post, x => x.CreateItem("aid", null));
        }
    }

//Test2 [Test] public void CreateItem_Exists2() {

            var config  = new HttpConfiguration();
            WebApiConfig.Register(HttpConfiguration); 
            var item = new Item();
           config
               .ShouldMap(string.Format("http://localhost/items/aid"))

                .WithBody(HttpUtility.UrlEncode(Json.Encode(item)))
                .To<temController>(HttpMethod.Post, x => x.CreateItem("aid", item ));
        }
    }

Both these tests fail with: System.ArgumentException was unhandled by user code HResult=-2147024809 Message=An item with the same key has already been added.

Why does this library loop through the object sent into a controller and add public properties to the expected Route values?


foreach (var field in objectFieldValues) { if(field.Value!=null) values.Add(field.Key.ToLowerInvariant(), field.Value); }

AnthonySteele commented 11 years ago

I have verified that that this test fails, The route param "id" clashes with the item field of the same name. Does this work correctly in the production code, given the name clash?

AnthonySteele commented 11 years ago

I have verified that this scenario does work in actual use - the ApiController can have two values both called "id", I assume that this works without a clash because one is on the url params, the other is from body.

AnthonySteele commented 11 years ago

I have checked in code that fixes this.

Now, some seemingly changes are one-liners, and some cause work throughout a lot of the code. This was one of the latter, as the use of IDictionary<string, string> to store expected and actual params did not allow duplicate keys (and was a "Primitive Obsession" http://c2.com/cgi/wiki?PrimitiveObsession ). suitable types - RouteValues and related types - have been made to hold this data.

I will roll this out in a nuget package soon.