You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Grails 6集成测试因找不到i18n消息失败,如何解决?

Grails 5.3.2升级至6.0.0后集成测试i18n消息解析异常问题

升级Grails应用从5.3.2到6.0.0后,我的110个集成测试中有26个出现i18n消息解析失败的问题,典型错误如下:

org.springframework.context.NoSuchMessageException: No message found under code 'itemDisplayDateTimeFormat' for locale 'en'.
    at org.springframework.context.support.AbstractMessageSource.getMessage(AbstractMessageSource.java:161)
    at com.hypercision.attproto.device.DeviceManagementController.getDataForDeviceApproval(DeviceManagementController.groovy:93)
    at com.hypercision.attproto.device.DeviceManagementController.getDataForDeviceApproval(DeviceManagementController.groovy)
    at org.grails.core.DefaultGrailsControllerClass$MethodHandleInvoker.invoke(DefaultGrailsControllerClass.java:223)

该问题在本地机器和CircleCI工作流中均会出现,我尝试删除项目根目录下的.gradle目录,但并未解决错误。


已解决的服务集成测试处理方式

我通过手动创建并注入StaticMessageSource(和单元测试中的做法一致),让一个服务集成测试恢复正常,代码如下:

@Integration
@Rollback
class SubmissionServiceIntegrationSpec extends Specification {

    @Autowired
    SubmissionService submissionService

    StaticMessageSource staticMessageSource = new StaticMessageSource()

    def setup() {
        staticMessageSource.addMessage("SubmissionService.submit.sessionAlreadySubmittedError",
                Locale.US, "session already submitted")
        submissionService.setMessageSource(staticMessageSource)
    }

    def setupData() {
        // 此处省略创建领域类的代码
    }
    
    def "test submitAllSessionsInGroup when the SessionItem is in a group of two and there are errors"() {
        given: "the SessionItem is in a SessionItemGroup that has 2 sessionItems"
        setupData()
        assert sessionItemGroup.getSize() == 2
        and: "One of the sessionItems is submitted"
        sessionItem1.status = "Submitted"
        assert sessionItem2.status == "Not Started"
        assert sessionItem2.submitTS == null

        when:
        submissionService.submitAllSessionsInGroup(sessionItem1)

        then:
        InvalidSessionItemStatusException exception = thrown()
        exception != null
    }
}

待解决的控制器集成测试问题

但我不清楚如何对如下控制器集成测试执行同样的操作:

// src/integration-test/groovy/com/hypercision/attproto/device/DeviceManagementControllerIntegrationSpec.groovy
package com.hypercision.attproto.device

import com.hypercision.attproto.instructor.Instructor
import com.hypercision.attproto.serviceaccount.ServiceAccount
import grails.gorm.transactions.Rollback
import grails.testing.mixin.integration.Integration
import groovy.json.JsonSlurper
import io.micronaut.http.HttpRequest
import io.micronaut.http.HttpResponse
import io.micronaut.http.HttpStatus
import io.micronaut.http.client.HttpClient
import io.micronaut.http.client.exceptions.HttpClientResponseException
import io.micronaut.http.uri.UriBuilder
import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Unroll

@Integration
@Rollback
class DeviceManagementControllerIntegrationSpec extends Specification {

    @Shared HttpClient client
    @Shared String accessToken
    @Shared Instructor instructor
    @Shared List<Device> devices = []

    void setup() {
        // 使用Bootstrap.groovy中创建的ServiceAccount
        ServiceAccount serviceAccount = ServiceAccount.first()
        assert serviceAccount
        instructor = new Instructor(userID: UUID.randomUUID().toString(), fullName: "Wendy",
                serviceAccount: serviceAccount).save(failOnError: true)
        Device device1 = new Device(deviceID: UUID.randomUUID().toString(), registrationCode: "111",
                platform: "iOS", platformVersion: "12.1.0", instructor: instructor).save(failOnError: true)
        Device device2 = new Device(deviceID: UUID.randomUUID().toString(), registrationCode: "222",
                platform: "Android", platformVersion: "9.0.0", instructor: instructor,
                approvalToken: "aaabbb123").save(failOnError: true)
        devices = [device1, device2]
    }

    void cleanup() {
        Device.deleteAll(Device.getAll())
        instructor.delete()
    }

    @Unroll
    void "test getDataForDeviceApproval returns 200"() {
        given:
        assert Device.count == 2
        final String uriPath = '/deviceManagement/getDataForDeviceApproval'
        String uri = UriBuilder.of(uriPath)
                .queryParam("token", devices[1].approvalToken)
                .queryParam("lang", lang)
                .build()

        when: "调用被测试的方法"
        HttpRequest request = HttpRequest.GET(uri)
        HttpResponse<String> resp = this.client.toBlocking().exchange(request, String)
        Map json = new JsonSlurper().parseText(resp.body()) as Map

        then: "返回成功状态"
        resp.status == HttpStatus.OK
        json != null
        json.id == devices[1].id
        json.deviceID == devices[1].deviceID
        json.platform == devices[1].platform
        json.approved == devices[1].approved

        and: "响应包含对应请求locale的正确日期格式"
        json.itemDisplayDateTimeFormat == format

        when: "使用错误的approval token再次调用方法"
        uri = UriBuilder.of(uriPath)
                .queryParam("token", "this_does_not_match_any_saved_devices")
                .queryParam("lang", lang)
                .build()

        request = HttpRequest.GET(uri)
        this.client.toBlocking().exchange(request, String)

        then: "返回400状态"
        HttpClientResponseException ex = thrown()
        HttpResponse<String> exResponse = ex.response as HttpResponse<String>
        Map exJson = new JsonSlurper().parseText(exResponse.body()) as Map
        exResponse.status == HttpStatus.BAD_REQUEST

        and:
        exJson != null
        exJson.errors != null
        exJson.errors.size() == 1
        exJson.errors[0].message == "Invalid token: device not found with that token."

        where:
        lang | format
        "en" | "MM/dd/yyyy HH:mm z"
        "en_GB" | "d MMM yyyy HH:mm z"
    }
}

更新

我创建了一个Grails 6.0.0示例项目,但并未重现该问题,因此很可能是我们应用自身的配置问题导致了NoSuchMessageException。


内容的提问来源于stack exchange,提问作者wubbalubba

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 21:07:31