views:

107

answers:

3

I've got a base controller that I inherit all of my Controllers from. It's job is to basically set caching and error handling as well as check for mobile browsers.

My UI works fine, but my Unit Tests are failing.

Imports System.Web.Mvc

<HandleError()> _
<CompressFilter()> _
<OutputCache(Duration:=30, VaryByParam:="id")> _
Public Class BaseController : Inherits System.Web.Mvc.Controller

    Protected Overrides Function View(ByVal viewName As String, ByVal masterName As String, ByVal model As Object) As System.Web.Mvc.ViewResult
        Dim ismobile As Nullable(Of Boolean) = Request.Browser.IsMobileDevice

        If ismobile Then
            Return MyBase.View(viewName, "Mobile", model)
        Else
            Return MyBase.View(viewName, "Site", model)
        End If

    End Function

End Class

The error I'm getting in my Unit test is on Dim ismobile As Nullable(Of Boolean) = Request.Browser.IsMobileDevice saying

Object Reference Not Set To An Instance Of An Object.

Edit:

Here is what my test class looks like

Imports System.Web.Mvc
Imports UrbanNow.Core
Imports Microsoft.VisualStudio.TestTools.UnitTesting

<TestClass()> Public Class EventsControllerTest

    <TestMethod()> Public Sub Index()
        ''# Arrange
        Dim controller As EventsController = New EventsController()

        ''# Act
        Dim result As ViewResult = CType(controller.Index(), ViewResult)

        ''# Assert
        Dim viewData As ViewDataDictionary = result.ViewData
    End Sub
End Class

It's pretty much just a rip off of the test that gets setup when you create a new MVC Web Application.

+1  A: 

Did you mock the request properly?

jfar
Possibly not. I've NEVER done unit testing before. I'll update my question with my test class
rockinthesixstring
+1  A: 

Assuming that Request.Browser is an extension property, it is the one of the logical places for an exception to be thrown. Alternatively, implementation of IsMobileDevice tries to access something that is null when run out of a unit testing environment.

Edit After looking at the test code, it seems the problem is that Request property on the controller is never set. When the page is executed by ASP .NET, the framework populates it with the valid object. When unit testing, you need to provide a mock for Request that does enough to make the test not crash.

There are two ways to do it:

  1. Create a class that derives from RequestBase and implement enough of it to make Browser not crash.
  2. Use a mocking framework (like Moq) to create a mock at runtime. You would still need to implement whatever functionality that make Browser.IsMobile actually work.

My suggestion would be to use Moq to mock Request to return another Moq mock that mocks Browser property. Then you can simply return true/false for IsMobileDevice, so that you can isolate the controller functionality without worrying about how Browser.IsMobileDevice works.

C# example code:

Mock<RequestBase> requestMock = new Mock<RequestBase>();
Mock<Browser> browserMock = new Mock<Browser>();
requestMock.Setup(request=>request.Browser).Returns(browserMock.Object);
browserMock.Setup(browser=>browser.IsMobileDevice).Returns(true);
controller.Request = requestMock.Object;
Igor Zevaka
The error is thrown on Request.Browser. I'm not sure how to account for that in my unit testing.
rockinthesixstring
Yep, that's because `BaseController.Request` is never set and is always null. See edit.
Igor Zevaka
@Igor in the second line i guess you are trying to declare browserMock?
vijaysylvester
Yes indeed, thanks for that, fixed now.
Igor Zevaka
A: 

There are two solutions for this.

  1. Mock the HTTPRequest instance as Igor mentioned.

  2. Set the required private set properties using reflection. This is extremely useful to inject some dependency directly into the subject that is being tested. (Mocking frameworks internally does this though) . This approach helps when you are not familiar with Mocking Frameworks.

E.g.,

  private static void SetPrivateSetPropertyValue(Type type, object obj, string fieldName, object value)
    {
        PropertyInfo propInfo = type.GetProperty(fieldName);
        propInfo.SetValue(obj, value, null);
    }

Consuming Code

E.g.,

this.SetPrivateSetPropertyValue(Request.GetType(),reqInstance,"Browser",browInstance);

Hope this helps,

Thanks, Vijay

vijaysylvester