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

MVC 5 #29

Closed AnthonySteele closed 10 years ago

AnthonySteele commented 10 years ago

Apparently MvcRouteTester the nuget package does not work with the latest release of MVC - VS2013 and MVC5 A nuget package can include multiple binaries for different .Net versions, but not for different dependant library versions.

People seem to get around this by making different packages, e.g. http://www.nuget.org/packages/StructureMap.MVC4/ or http://www.nuget.org/packages/Ninject.Web.Mvc2/

I think I should do the same, using git branches here on github to track the different versions.

It seems that the minimum .Net framework version for MVC5 is .Net 4.5

AnthonySteele commented 10 years ago

It's mostly working now, but there is this: http://stackoverflow.com/questions/20034983/mapmvcattributeroutes-this-method-cannot-be-called-during-the-applications-pre

AnthonySteele commented 10 years ago

I have put out a beta verions 0.0.1: http://www.nuget.org/packages/MvcRouteTester.MVC5/

AnthonySteele commented 10 years ago

Looks like getting Attribute routes working on Web controllers in ASP MVC 5 is going to be hard https://aspnetwebstack.codeplex.com/workitem/1445

Oddly, it works fine on Api controllers.

stevenkuhn commented 10 years ago

@AnthonySteele I don't know if it helps, but I was able to circumvent this exact problem. When I used dotPeek I discovered that there was an internal MapMvcAttributeRoutes extension method that took in an IEnumerable<Type> which is supposed to be a list of controller types. I'm not sure why that method isn't public instead of internal, but using reflection I was finally able to use MvcRouteTester with ASP.NET MVC 5. Here's my new extension method:

public static class RouteCollectionExtensions
{
    public static void MapMvcAttributeRoutesForTesting(this RouteCollection routes)
    {
        var controllers = (from t in typeof(HomeController).Assembly.GetExportedTypes()
                            where
                                t != null &&
                                t.IsPublic &&
                                t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) &&
                                !t.IsAbstract &&
                                typeof(IController).IsAssignableFrom(t)
                            select t).ToList();

        var mapMvcAttributeRoutesMethod = typeof(RouteCollectionAttributeRoutingExtensions)
            .GetMethod(
                "MapMvcAttributeRoutes",
                BindingFlags.NonPublic | BindingFlags.Static,
                null,
                new Type[] { typeof(RouteCollection), typeof(IEnumerable<Type>) },
                null);

        mapMvcAttributeRoutesMethod.Invoke(null, new object[] { routes, controllers });
    }
}

And here is how I use it:

public class HomeControllerRouteTests
{
    [Fact]
    public void RequestTo_Root_ShouldMapTo_HomeIndex()
    {
        // Arrange
        var routes = new RouteCollection();

        // Act
        RouteConfig.RegisterRoutes(routes);
        routes.MapMvcAttributeRoutesForTesting();

        // Assert
        routes.ShouldMap("~/").To<HomeController>(x => x.Index());
    }
}

The main issue now is that inside RouteConfig.RegisterRoutes(routes) I can't call routes.MapMvcAttributeRoutes(), but instead I moved that call to my Global.asax.cs file. It's a hack, but it was the only way I could get it to work with a minimal amount of work.

Again, I don't know if it helps at all, but I thought I would share it anyway.

AnthonySteele commented 10 years ago

I'm trying this out, thanks so much! It seems to work, I'll see if anything can be done about the usability https://github.com/AnthonySteele/MvcRouteTester/commit/6d6fcf587598b570e3fd8638d0b653114e53721e

stevenkuhn commented 10 years ago

Awesome! MapAttributeRoutesInAssembly() is a much better name than what I had. :+1:

My only concern is if the developers change/remove the method in RouteCollectionAttributeRoutingExtensions since it is internal. I suppose you could check if mapMvcAttributeRoutesMethod is null and give an appropriate error message if it is (e.g. "The implementation used for ASP.NET MVC 5 is no longer valid. Please update the MvcRouteTester NuGet package to get the latest version."). Hopefully by that time, the issue you mentioned above will have been resolved.

AnthonySteele commented 10 years ago

tidied up and pushed to nuget as MvcRouteTester.MVC5 v 0.0.2 http://www.nuget.org/packages/MvcRouteTester.MVC5/

AnthonySteele commented 10 years ago

chartek, would you like to put the code on the Stackoverflow question? IMHO it's good enough to be an accepted answer.

stevenkuhn commented 10 years ago

Cool, thanks! I went ahead and added the solution to Stackoverflow. Thanks for an awesome library! :smile:

WaltRitscher commented 10 years ago

@chartek How are you calling it from global.asax?