aspnet / Mvc

[Archived] ASP.NET Core MVC is a model view controller framework for building dynamic web sites with clean separation of concerns, including the merged MVC, Web API, and Web Pages w/ Razor. Project moved to https://github.com/aspnet/AspNetCore
Apache License 2.0
5.62k stars 2.14k forks source link

Ability to test actual routing without starting application/webserver #2096

Closed Rast1234 closed 9 years ago

Rast1234 commented 9 years ago

Hello. I'm using ASP.NET Web API 2 in production right now. At the beginning of this pleasant journey my team had issues with routing: when you add or modify routes, there is a chance to break currently working ones because of default parameters, one route being a substring of another and other ways. I searched for a way to simulate whole Web Api pipeline and observe route match results (controller and its method) without actually running an application. Finally I've digged into Web Api sources and made a RouteTester. It builds actual routes like Web Api does and tries to match given HttpRequestMessage to controller and method. It is also capable of calling the selected method, keeping the whole pipeline.

Is there any feature like this in the upcoming vNext? If no, i'd like to contribute :) The feature is important IMHO because you can write tests and be sure that your routing is not broken and every piece of magic works as you expect.

danroth27 commented 9 years ago

You can use TestServer to host an MVC app in memory and then run integration tests including routing in memory. Take a look at the following examples in the MVC tests: https://github.com/aspnet/Mvc/blob/dev/test/Microsoft.AspNet.Mvc.FunctionalTests/RoutingTests.cs.

Rast1234 commented 9 years ago

Thank you for the link. As far as i can see, running a TestServer does the thing. However, i have questions now: 1) With TestServer, you have to actually call an action, not just select it. What if you are testing just routes? //correct me if i'm wrong 2) Who does the magic of putting RoutingResult to response body? If you want to test actual response body, where to get it? 3) You compare result.action and result.controller with desired values as strings. Maybe it's better to compare actual Type of controller and MethodInfo of action instead of hard-coding? It is possible to get them from the ControllerDescriptor and ReflectedHttpActionDescriptor (I'm talking in terms of WebApi2, maybe now things are changed). For example, my tests look like this:

var requestString = string.Format("http://somehost/api/v1/debug/{0}", command);
        var request = new HttpRequestMessage(HttpMethod.Get, requestString);
        routeTester.Perform(request).CompareControllerAndMethod((DebugController x) => x.Get(command));
// threre are also CompareCode() and CompareContent() methods to test HTTP code and response. The action gets called only if you call one of them.

I had no idea how to test parameters binding, just can be sure that right action overload selected.

rynowak commented 9 years ago

Hi,

The design of routing has changed significantly since the system.web days, and doesn't really support a testing model like you may have used in WebAPI. If you take a gander at https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNet.Mvc.Core/MvcRouteHandler.cs you'll see that the entry point into MVC is itself an IRouter.

The main difference is that there's no longer a separation between routes and handlers. Everything that does routing is still on the call stack when you invoke a handler.

So, to answer your first question, there really is no testing 'just routes'. If you wanted to do something as close as possible, you could add a resource filter in startup code that would return information about what action was chosen (this is pretty similar to what our test code does.

public class RoutingDataFilter : IResourceFilter
{
    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        // get data into a format you like from context.RouteData.Values and context.ActionDescriptor
        var data = ...
        context.Result = new ObjectResult(data);
    }
}

To the second, I'm not sure what you mean exactly. These are normal action methods that return data, the JsonFormatter is writing it out.

Thirdly, these tests are written as client-server test. If you do a build in our repo you'll see them run on your box, but we have done the exercise of running them on a remote server - something we're ramping up as we get close to a v1 release. This isn't meant to be a pattern specifically for routing testing, it's across all of our functional/integration testing.

Rast1234 commented 9 years ago

Thanks for all the info!