PBMining

Searching...
Saturday, June 16, 2012

Easy way to Test Routes in asp.net mvc

10:27 AM

In my previous post I explain how to create advance routing system.but testing is one of thing that need in each and every step of application life cycle.so my this post show you how to test a routes easily using unit testing.

In web application all things begin with HttpContext.as Unit test is not a web application it does not use HttpContext but for testing route routes system need a URL and it ask for HttpContext.Request.AppRelativeCurrentExecutionFilePath.for that we have to create mock for HttpRequest,HttpResponse and we overrides a request for this property in HttpContextBase with mock of HttpRequestBase and HttpResponseBase. we use Moq tool for create mock of this classes.

  Private Function CreateHttpContext(Optional targerUrl As String = Nothing, Optional HttpMethod As String = "GET") As HttpContextBase
        'create mock request
        Dim mockRequest As New Mock(Of HttpRequestBase)
        mockRequest.Setup(Function(r) r.AppRelativeCurrentExecutionFilePath).Returns(targerUrl)
        mockRequest.Setup(Function(r) r.HttpMethod).Returns(HttpMethod)


        'create mock response
        Dim mockResponse As New Mock(Of HttpResponseBase)
        mockResponse.Setup(Function(r) r.ApplyAppPathModifier(It.IsAny(Of String))).Returns(Of String)(Function(s) s)



        'create httpMockContext using request and response mock
        Dim mockHttpContext As New Mock(Of HttpContextBase)
        mockHttpContext.Setup(Function(r) r.Request).Returns(mockRequest.Object)
        mockHttpContext.Setup(Function(r) r.Response).Returns(mockResponse.Object)


        Return mockHttpContext.Object



    End Function

Above function return fake copy of HttpContextBase class.first we have overrides the request for AppRelativeCurrentExecutionFilePath property of HttpRequestBase with user define URL values, and HttpMethod with the Optional GET string value. we have created Mock of HttpResponseBase it just simply return combined url with session id.don't be confuse with this it doesn't make much sense to our testing. and in the last we have just mock HttpContextBase and overrides the property Request,Response with our mocked HttpRequestBase,HttpResponseBase object.

when data is extracted from URL we have to check that extracted values that we expect.for that we have to create function so it can compare the route data with values we expect.

Private Function TestIncomingRouteResult(ByVal rd As RouteData, ByVal controller As String, ByVal action As String, Optional properties As Object = Nothing) As Boolean
        Dim valCompare As Func(Of String, String, Boolean) = Function(p1 As String, p2 As String)
                                                                 Return (StringComparer.InvariantCultureIgnoreCase.Compare(p1, p2) = 0)
                                                             End Function


        Dim result As Boolean = valCompare(rd.Values("controller"), controller) And valCompare(rd.Values("action"), action)

        If properties IsNot Nothing Then
            Dim propinfo() As PropertyInfo = properties.GetType.GetProperties

            For Each pi In propinfo
                If Not (rd.Values.ContainsKey(pi.Name) And valCompare(rd.Values(pi.Name), pi.GetValue(properties, Nothing))) Then

                    result = False

                    Exit For

                End If

            Next


        End If
        Return result
    End Function

In Above code first we check for controller and action values of RouteData is matched with the values that we passed as a parameter. after that we check for the extra parameters that we passed using URL like Query String or id value. any of this comparison get failed function return false and test get failed.

for testing purpose we have created two method. one for check Route should matched and another for checking that route should failed.

    Public Sub TestRouteMatch(ByVal url As String, ByVal controller As String, ByVal action As String, Optional routeProperties As Object = Nothing, Optional httpMethod As String = "GET")
          'Arrange
          Dim routes As New RouteCollection
          MvcApplication.RegisterRoutes(routes)

          'Act
          Dim data As RouteData = routes.GetRouteData(CreateHttpContext(url, httpMethod))

          'Assert
          Assert.IsNotNull(data)
          Assert.IsTrue(TestIncomingRouteResult(data, controller, action, routeProperties))


      End Sub

      Public Sub TestRouteFail(ByVal url As String, Optional ByVal httpmethod As String = "GET")
          'arrange
          Dim routes As New RouteCollection
          MvcApplication.RegisterRoutes(routes)

          'Act
          Dim data As RouteData = routes.GetRouteData(CreateHttpContext(url, httpmethod))

          'Assert
          Assert.IsTrue(data Is Nothing OrElse data.Route Is Nothing)

      End Sub


In both of above method first we force MVC to register available routes.next we create mocked HttpContext using URL and HttpMethod and pass it to GetRouteData function of RouteCollection. and we check for the data that is extracted from the URL using method TestIncomingRouteResult.depend on the result of that method Test get Pass or Fail.

Below route definition and testing is just for your reference purpose of how to test routes with above mechanism.because for real testing you have to check route one by one by commenting some other routes.

    Public Sub TestIncomingRoutes()

    TestRouteMatch("~/RouteTest/Index", "RouteTest", "Index") 'Match by R1,R2
    TestRouteMatch("~/", "RouteTest", "Index") 'by R2

    TestRouteFail("~/RouteTest/Index/Segment")
    TestRouteFail("~/RouteTest") 'passed if R1 route is not present

    'route with url segment
     TestRouteMatch("~/", "RouteTest", "Index", New With {.id = "someValue"}) 'match by R7
     TestRouteMatch("~/RouteTest", "RouteTest", "Index", New With {.id = "somevalue"}) 'match by R7
     TestRouteMatch("~/RouteTest/Index", "RouteTest", "Index", New With {.id = "somevalue"}) 'match by R7
     TestRouteMatch("~/RouteTest/Index/somevalue", "RouteTest", "Index", New With {.id = "somevalue"}) 'match by R7
     TestRouteFail("~/RouteTest/Index/somevalue/somevalue2") 'not match by R7
    


    End Sub

    'define routes in global.asax

    Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
           routes.IgnoreRoute("{resource}.axd/{*pathInfo}")

    routes.MapRoute("R2", "{controller}/{action}", New With {.controller = "RouteTest", .action = "Index"})
    routes.MapRoute("R1", "{controller}/{action}", New With {.action = "Index"})
    routes.MapRoute("R7", "{controller}/{action}/{id}", New With {.controller = "RouteTest", .action = "Index", .id = "somevalue"})

   End Sub

0 comments:

Post a Comment

Write your review about this post.