views:

204

answers:

2

Hi,

I'm just getting going with ASP.NET MVC and I'm also new to unit testing :) So far, so good.

I have a controller action that sets up an index view using a viewmodel. Testing the controller action is straight-forward as I can pass a fake service class in the controller's constructor, but my viewmodel is quite complex and fetches it's own service class on instantiation.

Code should make this clearer I hope...

Controller action:

Function Index(ByVal id As Integer?) As ActionResult
  Dim totalCount As Integer = 0
  Dim selectedClient As Integer
  If id Is Nothing Then
    selectedClient = _portalClientService.GetFirstClient().ID
  Else
    selectedClient = id
  End If
  Dim users As MembershipUserCollection = _membershipService.GetUsersByClientId(selectedClient, 0, 1000, totalCount)
  Return View(New UserListViewModel(users, selectedClient))
End Function

Viewmodel class:

Public Class UserListViewModel

  Private _clientService As IPortalClientService

  Public Sub New(ByVal users As MembershipUserCollection, ByVal selectedClient As Integer)
    Me.New(users, selectedClient, Nothing)
  End Sub

  Public Sub New(ByVal users As MembershipUserCollection, ByVal selectedClient As Integer, ByVal clientService As IPortalClientService)
    _users = users
    _clientService = If(clientService, New PortalClientService)
    _clients = New SelectList(_clientService.GetClients.OrderBy(Function(c) c.ClientName), "ID", "ClientName", selectedClient)
  End Sub

  Private _users As MembershipUserCollection
  Public Property Users() As MembershipUserCollection
    Get
      Return _users
    End Get
    Set(ByVal value As MembershipUserCollection)
      _users = value
    End Set
  End Property

  Private _clients As SelectList
  Public Property Clients() As SelectList
    Get
      Return _clients
    End Get
    Set(ByVal value As SelectList)
      _clients = value
    End Set
  End Property

End Class

EDIT:

When testing the controller action, how do I get the viewmodel to use a fake service class?

Should I just ditch the first constructor and always pass in the service from the controller or is there another way?

Cheers,
Nick

A: 

Actually, this is a pattern that we use all the time in our public facing API's, and demonstrates good use of dependency injection. I would pass this in a code review with no problems.

Your implementation gives the user the option to flexibly create the object, and provides for testability.

The only "problem" is that your tests can't easily cover the one line of code in the first constructor, but that's only a problem if you have someone who's fanatical about code coverage - which is usually a problem in itself.

womp
Thanks, it's good to know I'm not too far out :)The main problem is that in unit testing, I can pass a fake clientservice class to the controller, but the viewmodel instantiates it's own service class and hits the database.Without modifying the controller action, is there any way I can get the viewmodel to use a fake service class when I'm testing the controller action?
Nick
+3  A: 

I am probably splitting hair, but I would say your model is more a domain model than a view model. Remove the dependency to the IPortalClientService, or at least do not let the model instantiate it by itself.

I prefer to remove such dependencies away from the view, and over to the controller.

Thomas Eyde