Unit testing controller actions with complex viewModels - asp.net-mvc

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

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.

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.

Related

Integration Tests in grails: getting null in query string parameter

I'm doing integration tests for controllers in grails.I need to pass query string in test case and I'm passing it as controller.request.queryString for the same.
My tests is getting failed. It's giving me null value for the query String parameter.
Controller - UserExController
Foll is the action on which i'm doing integration testing.In this I'm reading query string parameter 'uid' and getting the userInstance using GORM method
def getUser() {
//reading the query string parameter 'uid'
def userId = request.getParameter("uid")
User user = User.findByUsername(userId)
render view:'edit', model:[userInstance:user]
}
This is the test for the above written action .In this I'm passing the query string parameter and calling the action
class UserInstanceTests extends GroovyTestCase {
#Test
void testPerfSummaryApi() {
def userExController=new UserExController()
userExController.request.queryString='uid=rp123'
userExController.getUser()
def model = userExController.modelAndView.model.userInstance
assertNotNull model
}
}
I'm getting groovy.lang.MissingMethod.Exception in the foll call:
User.findByUsername(userId) //userId is comming null
Change in Controller:
def userId = request.getParameter("uid")
to
def userId = params.uid
Change in int test:
userExController.request.queryString='uid=rp123'
to
userExController.params.uid='rp123'
Grails' dynamic binding helps in binding the query parameters to params and request to request in the controller.
This should make things work for you (tested on Grails 2.0.4):
userExController.request.addParameter('uid', 'rp123')
However, I would recommend to turn to dmahapatro's suggestion and refactor your controller to use params.uid instead of request.getParameter("uid"). Or even introduce a command object. The way you are doing it now just does not seem to be the standard Grails way.

How to loop through Membership List?

I have a list of all memberships:
Dim allUsers = Membership.GetAllUsers().Cast(Of MembershipUser).ToList
Despite my searches, I have been unable to figure out how to loop through this to do something like the following. I want to loop through and retrieve a record, if the record contains a certain value, add the user to a new collection.
for each allusers as user
' get user profile
' if user profile has a certain value, add it to another collection
end for each
Then, once the new collection is full, output that collection to my view.
Does that make sense? How can I do this? Thank you.
Try this:
Public Function GetUsers() As IEnumerable(Of MembershipUser)
Dim newListOfUsers = New List(Of MembershipUser)()
Dim users As MembershipUserCollection = Membership.GetAllUsers()
For Each user As MembershipUser In users
If user.Comment = "A Leather Glove" Then
newListOfUsers.Add(user)
End If
Next
Return newListOfUsers
End Function
Added C# version:
var newListOfUsers = new List<MembershipUser>();
Membership.GetAllUsers().OfType<MembershipUser>().ToList().ForEach( user =>
{
if(user.Comment.Equals("A Leather Glove")) newListOfUsers.Add(user);
});

How to access property from grails query

I have a domain class called Application as follows:
class Application {
static hasOne = [resumption:Resumption, employee:Employee]
//Employee employee
Date startDate
Date endDate
Integer amountOfDays
String leaveRecommended
String leaveNotRecommended
Date supervisorDate
String toString(){
return "Application for ${employee.lastName}, ${employee.firstName}"
}
}
In the ApplicationController I'm trying to write a query that is going to find all applications that match a particular employee id. I do so as follows:
def applicationlist(){
if(!params.max){
params.max = 10
}
def query
def criteria = Application.createCriteria()
def results
query = { eq("employee_id", Long.parseLong("1")) }
results = criteria.list(params, query)
render(view:"employeeapplicationlist", model:[applicationlist:results])
}
Now I keep getting the error: "could not resolve property: employee_id"
I've checked the generated Application table in MySql, there is a column called employee_id with a value. The weird thing is I can access any other property (like amountOfDays), so what's the deal with employee_id? Why is it complaining that it cannot resolve the property? What am I missing? Thanks in advance.
Associations in the criteria DSL are of the form
Application.withCriteria{
employee{
eq 'id', 1
}
}
http://grails.org/doc/latest/guide/GORM.html#criteria
But you could probably just do:
def employee = Employee.proxy(1)
Application.findAllByEmployee( employee )
This appears a few times in the Grails User Guide as 'querying associations'
Oh well it looks like I'm still not fully adjusted to interfacing with the database on an Object level. For anyone else with this or a similar problem, here's the fix:
query = { eq("employee.id", Long.parseLong("1")) }
Sine the Application Domain class has one Employee, then we just need to access the id field of that employee. Remember we're in the ApplicationController.

Rendering a textbox for a complex type?

I have been reading the various blog posts on how to deal with a Complex Type in EF including the brad wilson post which most everyone seems to link. owever i dont really get it. I am using EF code first development, MVC and VB. From what i have read so far to render a editor field for a complex types requires a custom object, right? However i dont really understand what code i need to put into the custom template. Can someone break it down for me as to what code needs to go into custom templete so i can render a textbox for the PostTags icollection?
My classes:
Public Class Post
Inherits EntityBase
<Key()> Property PostId As Integer
<DisplayName("Title")> <Required()> Property PostTitle As String
<UIHint("MultilineText")> <DisplayName("Text")> Property PostText As String
ReadOnly Property PostExcerpt As String
Get
If PostText.Length > 100 Then
Return Helpers.TruncateHelper.TruncateAtWord(PostText, 250)
Else : Return PostText
End If
End Get
End Property
<ScaffoldColumn(False)> Property PostDateCreated As DateTime
<ScaffoldColumn(False)> Property PostDateModified As DateTime?
<ScaffoldColumn(False)> Property PostDatePublished As DateTime?
<DisplayName("Publish?")> Property PostIsPublished As Boolean
<DisplayName("Allow Comments?")> Property CommentsAllowed As Boolean
<ScaffoldColumn(False)> Property CategoryId As Integer?
<UIHint("Text")> <DisplayName("Category")> <Required()> Property PostCategory As String
Property Comments As IList(Of Comment)
'Post has collection of Tags
<DisplayName("Tags (comma separated)")> Overridable Property PostTags As ICollection(Of Tag)
End Class
Public Class Tag
Dim _rdsqlconn As RDSQLConn
<Key()> Property TagId As Int32
Property PostId As Int32
Property TagWord As String
'Overridable property to access Post
Overridable Property Post As Post
End Class
Solved.
Created the template Tags.vbhtml in the EditorTemplates folder and applied to PostTags. the template has the textbox which is shown in the view.

many-to-many, poco, ef4

I have 3 entities:
Goods [GID(PK), GoodName]
Persons [PID(PK), PersonName]
Roles [RID(PK), RoleName]
But now I need to associate these object with each other.
In other words, each Good can have MANY Persons in MANY Roles.
I have a table in DB with 3 fields (GID, PID, RID)
For example:
Book (GID#1), can have 3 associated persons:
1. Jack (PID#1) in role Author (RID#1)
2. Jack (PID#1) in role Editor (RID#2)
3. Bill (PID#2) in role Painter (RID#3)
How can I map this in POCO format in Entity Framework 4?
I believe you have to create another PersonRoles header where you store the Person-Role relation, then you access the person+role via this one:
PersonRoles [PRID(PK), PersonName, RoleName] (note: you could also do this withnow entitykey, just relationships, EF will eliminate this entity and give a direct Person.Roles entity, which you can do access it via Book.Persons.Select((p) p.Roles)).
PersonRole#1: Jack#1/Author#1
PersonRole#2: Jack#1/Editor#2
PersonRole#3: Bill#2/Painter#3
Book.PersonRole = context.PersonRoles.
SingleOrDefault((pr) => pr.Person.PersonId == 1 && pr.RoleId == 1);
Note: my main lang is VB.NET so I apologize for the pseudu code above, but I hope you get the idea.
Update
It should be like:
<DataContract(IsReference:=True)>
<KnownType(GetType(Good))>
<KnownType(GetType(Person))>
<KnownType(GetType(Role))>
Partial Public Class GoodPersonRole
Implements IObjectWithChangeTracker
Implements INotifyPropertyChanged
<DataMember()>
Public Property GoodId() As Integer
Get
Return _goodId
End Get
Set(ByVal value As Integer)
If Not Equals(_goodId, value) Then
If ChangeTracker.ChangeTrackingEnabled AndAlso ChangeTracker.State <> ObjectState.Added Then
Throw New InvalidOperationException("The property 'GoodId' is part of the object's key and cannot be changed. Changes to key properties can only be made when the object is not being tracked or is in the Added state.")
End If
If Not IsDeserializing Then
If Good IsNot Nothing AndAlso Not Equals(Good.GoodId, value) Then
Good = Nothing
End If
End If
_goodId = value
OnPropertyChanged("GoodId")
End If
End Set
End Property
Private _goodId As Integer
<DataMember()>
Public Property Good() As Good
Get
Return _good
End Get
Set(ByVal value As Good)
If _good IsNot value Then
If ChangeTracker.ChangeTrackingEnabled AndAlso ChangeTracker.State <> ObjectState.Added AndAlso value IsNot Nothing Then
' This the dependent end of an identifying relationship, so the principal end cannot be changed if it is already set,
' otherwise it can only be set to an entity with a primary key that is the same value as the dependent's foreign key.
If Not Equals(GoodId, value.GoodId) Then
Throw New InvalidOperationException("The principal end of an identifying relationship can only be changed when the dependent end is in the Added state.")
End If
End If
Dim previousValue As Good = _good
_good = value
FixupGood(previousValue)
OnNavigationPropertyChanged("Good")
End If
End Set
End Property
Private _good As Good
End Class
(part from the generated entity by ADO.NET VB POCO Entity Generator)
I just copied the 'Good' Id and nav. property but it should be the three of them.

Resources