是否可以在同一单元测试中模拟控制器并访问 FormTagLib?

Is it possible to mock a controller and have access to the FormTagLib in the same unit test?

如标题所述,是否可以对控制器进行单元测试并模拟标签库?

就目前而言,我有一个用户控制器。许多动作使用

g.message(code: 'something.something')

调用以在页面上设置消息。

@TestFor(UserController)
@Mock(User)
@TestMixin(GroovyPageUnitTestMixin)
class UserControllerSpec extends Specification
{
    UserService userServiceMock = Mock(UserService)

def setup()
{
    controller.userService = userServiceMock
}

def cleanup()
{
}

void "test manageDevice"()
{
    given:
        def g = mockTagLib(FormTagLib)
    when:
        controller.manageDevice()
    then:
        model.pageTitle == 'message.device'
}

使用该代码,我试图点击控制器操作,但由于 g.message,它失败了,并显示一条错误消息,指出它无法将值设置为 null。几乎是因为它没有看到 "g.message"

我有点不确定我的单元测试是否需要以不同的方式编写,或者我是否只是遗漏了一些东西。

任何帮助都会很棒!

编辑:

使用 messageSource 的一些更新:

    void "test manageDevice"()
    {
    given:
        messageSource.addMessage 'user.devices', request.locale, 'Manage Devices'
    when:
        controller.manageDevices()
    then:
        assertEquals model.pageTitle == 'user.devices', controller.flash.message
    }

它似乎还在抱怨,因为它在控制器上没有 "g" 命名空间的上下文。我还要注意,我没有来自 messageSource 的 'addMessage' 上下文。不知道为什么,它应该在那里。

在同一个控制器和许多其他控制器中,我们在控制器范围内使用的唯一标签库是 'g' 用于每个操作中的 'g.message' 集。唯一正在进行的其他调用是使用 'g' 进行调用,例如 'g.fixRedisIssue'

为什么不return消息代码而不是return将消息文本从控制器发送到视图?例如,如果您的视图中有这样的内容:

<p>${flash.message}</p>

您可以将其替换为:

<p><g:message code="${flash.code}" /></p>

然后在控制器中设置代码:

flash.code = "user.devices"

然后您将能够轻松地测试控制器方法:)

您可以通过在规范 class 上的 grails.test.mixin.Mock 注释的值中包含必要的 TagLib classes 来使用模拟的 TagLib 对控制器进行单元测试。例如,如果您有以下 TagLib:
(基于 Grails documentation 中的代码)

package org.grails.samples

class SimpleTagLib {
    static namespace = 'g'
    def hello = { attrs, body ->
        out << "Hello ${attrs.name ?: 'World'}"
    }
}

并且您有以下控制器:

package org.grails.samples

class SimpleController {
    def flashHello() {
        flash.message = g.hello()
    }
}

您可以使用以下规格进行测试:

package org.grails.samples

import grails.test.mixin.Mock
import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(SimpleController)
@Mock(SimpleTagLib)
class SimpleControllerSpec extends Specification {
    void 'test flashHello'() {
        when:
        controller.flashHello()
        then:
        flash.message == 'Hello World'
    }
}

如果您有多个 class 要模拟,您可以在 @Mock 的数组参数中指定它们。因此,您可以通过将 @Mock(User) 更改为 @Mock([User, FormTagLib, YourFixRedisIssueTagLib]) 之类的内容,将任何必要的标签库添加到 UserControllerSpec。但是,如果您确实需要添加 FormTagLib,我会感到惊讶,因为在我的 Grails 测试中它似乎默认包含。

作为进一步的建议,您发布的某些代码没有任何意义。具体来说:

assertEquals model.pageTitle == 'user.devices', controller.flash.message

具体确定要测试的内容,并专注于为此编写尽可能简单的代码。

此外,根据您对所发生情况的描述,我认为您被抛弃了,因为您的 IDE 没有与 Grails 正确集成以查看 g 上下文。