Grails Mocks:从控制器中解耦验证

时间:2013-10-22 20:25:29

标签: unit-testing validation grails gorm

我有几个grails控制器,我生成并略微修改。我正在使用生成的单元测试并让它们通过,但我认为我正在以艰难的方式进行。这就是我所拥有的。

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() {
    ...
}

...

我希望能够动态模拟域对象,即expect(Attendance.save()).andReturn(null)expect(Attendance.save()).andReturn(testAttendance),这样我就不必在setUp方法中创建关联对象的Web了必须验证控制器正在操作的域对象。

我只是看着这一切都错了吗?看起来我应该能够将控制器逻辑与验证逻辑分离。这样我就可以告诉模拟器告诉控制器验证通过或失败。提前谢谢。

2 个答案:

答案 0 :(得分:1)

在模拟单元测试时,您不必拥有包含测试单个域所需的每个所需值的完整对象图。例如,你可以有这样的东西......

def department = new Department(name: "Accounting").save(validate: false)
def user = new User(username: "gdboling", department: department).save()

假设User只有2个必填字段是用户名和部门,但是部门可能还有许多其他字段无法通过验证,如果您真正需要测试的只是用户,这仍然有效。

您仍然需要在@Mock中指定它们,您不必填充每个血腥字段。 :)

答案 1 :(得分:1)

我认为没有办法告诉模拟器控制器处理的某个对象的验证通过或失败,但我可能错了。但据我所知,你主要担心的是创建相关对象的网络吗?

在不知道你的控制器是什么样的情况下,我猜你会通过ID(例如位置)在控制器中获取所需的域对象,并通过pidm加载Person等等。

要简化所需域对象的创建,可以使用 .save(validate:false)。 你的setUp方法可能如下所示:

location = new Location().save(validate: false)
reason = new Reason().save(validate: false)

如果您只需要具有有效ID的对象,这就足够了。

new Person(pidm: 12345).save(validate: false)
new UserAccount(username: "testuser").save(validate: false)

将某些字段设置为能够使用像UserAccount.findByUserName()这样的查找器。

因此,如果您的控制器执行类似

的操作
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, ...)

上述行应该适合您的setUp方法。

.save(validate:false)对于设置测试中真正需要的值非常有用。我希望我能把所有事情做对,我可以帮忙。