I have a several grails controllers that I generated and modified slightly. I'm working with the generated unit tests and getting them to pass, but I think I'm doing it the hard way. This is what I have.
package edu.liberty.swiper
import grails.test.mixin.*
import org.junit.*
@TestFor(AttendanceController)
@Mock([Attendance, Location, Reason, Person, LocCapMode, GuestContactMode, UserAccount])
class AttendanceControllerTests {
def location
def reason
void setUp() {
def capMode = new LocCapMode(description: "loc cap mode", username: "testuser").save(failOnError: true)
def guestMode = new GuestContactMode(description: "Guest Contact Mode", username: "testuser").save(failOnError: true)
location = new Location(description: "foo", locCapMode: capMode, username: "testuser", guestContactMode: guestMode).save(failOnError: true)
reason = new Reason(description: "test reason", username: "testuser").save(failOnError: true)
def person = new Person(firstName: "John", lastName: "Smith", lid: "L12345678", mi: "Q", pidm: 12345).save(failOnError: true)
def userAccount = new UserAccount(pidm: 12345, username: "testuser").save(failOnError:true)
}
def populateValidParams(params) {
assert params != null
params.personId = '12345'
params.username = "testuser"
params["location.id"] = location.id
params["reason.id"] = reason.id
params.timeIn = new Date()
}
void testIndex() {
...
}
void testList() {
...
}
void testCreate() {
...
}
void testSave() {
controller.save()
assert model.attendanceInstance != null
assert view == '/attendance/create'
response.reset()
populateValidParams(params)
controller.save()
assert response.redirectedUrl == '/attendance/show/1'
assert controller.flash.message != null
assert Attendance.count() == 1
}
void testEdit() {
...
}
...
What I'm hoping for is the ability to dynamically mock domain object, i.e. expect(Attendance.save()).andReturn(null)
or expect(Attendance.save()).andReturn(testAttendance)
, so that I don't have to create the web of associated objects in my setUp method that are necessary to validate the domain object that is being manipulated by the controller.
Am I just looking at this all wrong? It seems like I should be able to decouple the controller logic from the validation logic., so that I can just tell the mock to tell the controller that validation passed or failed. Thanks in advance.
I don't think there is a way to tell the mock that the validation of a certain object that is handled by a controller passed or failed but I might be wrong. But as I understand it your main concern is the creation of the web of associated objects right?
Without knowing what your controller looks like I would guess that you are getting needed domain objects in your controller by ID (e.g. Location) and load a Person by pidm and so on.
To simplify the creation of needed domain objects you could use .save(validate: false). Your setUp method could look like this:
location = new Location().save(validate: false)
reason = new Reason().save(validate: false)
If you only need objects with valid IDs this would be sufficient.
new Person(pidm: 12345).save(validate: false)
new UserAccount(username: "testuser").save(validate: false)
Set certain fields to be able to use a finder like UserAccount.findByUserName().
So if your controller does something like
location = Location.get(params["location.id"])
reason = Reason.get(params["reason.id"])
userAccount = UserAccount.findByUserName(params.username)
...
new Attendance(location: location, reason: reason, userAccount: userAccount, ...)
the aforementioned lines should be satisfactory for your setUp method.
.save(validate: false) is very useful to just set values that are really needed in your test. I hope I got the whole thing right and I could be of help.