简单 grails 控制器单元测试中的空指针

Posted

技术标签:

【中文标题】简单 grails 控制器单元测试中的空指针【英文标题】:Nullpointer in simple grails controller unit test 【发布时间】:2014-08-28 14:57:32 【问题描述】:

我需要一些帮助来解决我在对一个非常基本的 Grails 2.4.1 控制器进行单元测试时遇到的一个奇怪问题。

给定这个控制器:

class AuthenticationEventController 
    def index() 
        // Sorry, ajax only!
        if(!request.xhr) 
            redirect(controller: "main")
            return false
        

        render(template: "index")
        return
    

还有这个测试:

@TestFor(AuthenticationEventController)
class AuthenticationEventControllerSpec extends Specification 

    void "Test that the index rejects non-ajax calls"() 
        given:
            request.isXhr =  false 

        when:
            controller.index()

        then:
            response.redirectedUrl == '/main'
    

我在“controller.index()”调用中收到 NullPointerException。

java.lang.NullPointerException
    at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:130)
    at org.codehaus.groovy.grails.orm.support.GrailsTransactionTemplate.execute(GrailsTransactionTemplate.groovy:85)
    at au.com.intrinsicminds.advansys.controller.AuthenticationEventControllerSpec.Test that the index rejects non-ajax calls(AuthenticationEventControllerSpec.groovy:17)

【问题讨论】:

您的测试在 grails 2.3.8 中通过(我还没有 2.4.1),所以也许它是 grails 缓存的东西 - 如果您还没有尝试过 grails clean 的话?否则,可能值得看看 Grails 2.4 中的变化 为了让它在 2.4.1 中工作,我必须在测试中添加一个空的 def setup() 和 def cleanup() 方法,否则我得到方法未找到错误,也许你值得一试吗? 2.4.0 和 2.4.1 都有一些严重的问题,这些问题在 2.4.2 中得到了解决。我没有机会尝试您的测试,但如果可能,我会考虑迁移到 2.4.2。 你不应该需要一个空的setup()cleanup() 方法,你不应该升级到2.4.2 来测试它。 github.com/jeffbrown/xhrtest 的项目包含我粘贴到下面答案中的代码,并且该测试似乎有效。 【参考方案1】:

问题很可能是您正在使用

import grails.transaction.Transactional

而不是

import org.springframework.transaction.annotation.Transactional

用于 groovy 类中的 @Transactional 注释。

为什么,对于主要区别或为什么测试对此效果不佳没有明确的答案。 此外,这通常仅在您测试一个具有 2 个以上类层的类时才会发生。

【讨论】:

就我而言,我在来自 grails/src 的 spring bean 中使用 grails.transaction.Transactional。我将它切换为使用 spring 的 @Transactional,它似乎已经解决了这个问题。 我在测试中也遇到了这个问题,但我的问题是我使用 grails.transaction.Transactional 时没有从 spring 上下文中获取对象。我只是为测试创建对象,因为我不需要我正在测试的部分的依赖项。使用 spring 注释将在测试时起作用,但在运行时不起作用,而 grails 注释会发生相反的情况。我不得不返回测试并允许 spring 上下文注入我正在测试的对象。 这个修复也适用于我。在我使用invokeMethod 在元编程中找到NPE 之后。如果没有 NPE,grails.transaction.Transactional 一切正常【参考方案2】:

您是否在代码中的其他任何地方使用域类?我遇到了同样的问题(TransactionTemplate#execute 引发的 NPE),解决方案是根据这个 jira 问题将@Mock 用于我的一个实体:https://jira.grails.org/browse/GRAILS-11045

【讨论】:

【参考方案3】:

以下内容适用于 Grails 2.4.1。

控制器:

// grails-app/controllers/demo/AuthenticationEventController.groovy
package demo

class AuthenticationEventController 
    def index() 
        if(!request.xhr) 
            redirect(controller: "main")
         else 
            render(template: "index")
        
    

单元测试:

// test/unit/demo/AuthenticationEventControllerSpec.groovy
package demo

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

@TestFor(AuthenticationEventController)
class AuthenticationEventControllerSpec extends Specification 

    void "Test that the index redirects for non-ajax calls"() 
        when:
        controller.index()

        then:
        response.redirectedUrl == '/main'
    

    void "Test that index renders template for ajax calls"() 
        given:
        request.makeAjaxRequest()
        views['/authenticationEvent/_index.gsp'] = 'my template text'

        when:
        controller.index()

        then:
        response.contentAsString == 'my template text'
    

希望对你有帮助。

【讨论】:

【参考方案4】:

我在尝试 Spy Transactional 服务时得到了相同的堆栈跟踪。

我找到了对我有帮助的解决方案。所以我只是在间谍服务中初始化 transactionManager。

参见示例:

SomeTransactionalService sts = Spy(SomeTransactionalService)
sts.transactionManager = transactionManager // so you need to add  init transactionManager

【讨论】:

以上是关于简单 grails 控制器单元测试中的空指针的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Grails 单元测试中使用 Spock 模拟 passwordEncoder

单元测试 Grails 控制器链接

如何解决spring boot test中的空指针异常?

SpringBug记录 -- java.lang.NullPointerException在Spring单元测试中遇到的空指针异常及依赖注入异常总结

Grails 单元测试问题

单元测试控制器时无法模拟 Grails 服务方法 - MissingMethodException