【问题标题】:Grails unit tests: Accessing defined beans via grailsApplicationGrails 单元测试:通过 grailsApplication 访问定义的 bean
【发布时间】:2026-02-22 13:40:01
【问题描述】:

我有一些(非 Grails 工件)类通过传递 grailsApplication 对象来访问服务层 bean。但是,我无法对以这种方式实现的类进行单元测试。为什么 bean 没有在主上下文中注册?

@TestMixin(GrailsUnitTestMixin)
class ExampleTests {
  void setUp() {}

  void tearDown() {}

  void testSomething() {
    defineBeans {
      myService(MyService)
    }

    assert grailsApplication.mainContext.getBean("myService") != null
  }
}

上面的代码失败了:

org.springframework.beans.factory.NoSuchBeanDefinitionException:没有定义名为“myService”的bean

我正在尝试通过 grailsApplication 从普通的旧 Java 类访问服务。这有效,但不适用于单元测试环境。我应该采取不同的做法吗?

class POJO {
  MyService myService;

  public POJO(GrailsApplication grailsApplication) {
    myService = (MyService) grailsApplication.getMainContext().getBean("myService");
  }
}

【问题讨论】:

    标签: grails grails-2.0


    【解决方案1】:

    答案是在GrailsUnitTestMixin 中,保存你的bean 的applicationContext 被设置为grailsApplication 中的parentContext

    beans.registerBeans(applicationContext)
    
    static void initGrailsApplication() {
    ...
    //the setApplicationContext in DefaultGrailsApplication set's the parentContext
    grailsApplication.applicationContext = applicationContext
    }
    

    所以你可以得到你的豆子:

    defineBeans {
      myService(MyService)
    }
    
    assert applicationContext.getBean("myService")
    assert grailsApplication.parentContext.getBean("myService")
    

    编辑

    今天我遇到了同样的问题,我的解决方案是:

    @Before
    void setup() {
      Holders.grailsApplication.mainContext.registerMockBean("myService", new MyService())
    }
    

    【讨论】:

    • 感谢您的回答!我用一个示例编辑了我的问题,该示例说明了我如何尝试在我的 Java 类中获取 bean。这适用于正常环境,但不适用于单元测试。我应该在 parentContext 而不是 mainContext 中搜索所需的 bean,还是两者都搜索?您能否指出有关 parentContext 与 mainContext 的更多信息?
    • 好问题。我在 grails 邮件列表中复制了这个,让我们看看他们可以添加什么。在我编写的一些代码中,我也在使用 mainContext,可能会发现这是一些示例,但在这种情况下,我总是使用集成测试。
    • 我发现in the docs parentContext 和 mainContext 之间的区别,仍在等待确认,但我认为您的单元测试应该可以工作,所以可能是 Grails 的错误。
    • 在这种情况下你可以做的是模拟你获得服务的方式,虽然是 groovy 元类,而不是在你的构造函数中修复它。或者将你的类声明为 spring bean,允许依赖注入。
    • 嗨,看我的编辑。我今天需要这个并找到解决方案。
    【解决方案2】:

    在我的情况下(grails 2.4.4),接受的解决方案不起作用,但为我指明了正确的方向,这条线改为起作用,因为我的单元测试中 mainContext 中的 bean 工厂是 OptimizedAutowireCapableBeanFactory

    Holders.grailsApplication.mainContext.beanFactory.registerSingleton('myBean', new MyBeanClass())
    

    【讨论】:

    • 用 grails 2.5.1 测试并且可以工作(接受的解决方案必须适用于早期版本的 grails 并且没有工作)。谢谢!
    • 也为我工作。
    【解决方案3】:

    我在同样的问题上花了一些时间,在我的例子中运行 grails 2.2.4 并拥有(在 src/groovy 中):

    import grails.util.Holders
    class SomeClass {
      transient myService = Holders.grailsApplication.mainContext.getBean 'myService'
      .....
    }
    

    这与问题作者有点不同,但至少对来自搜索引擎结果的人有用

    尽管如此,接受的答案对我不起作用,所以我想出了一些不同的方法来模拟和注册 SomeClass 中使用的服务。

    import grails.util.Holders
    .. other imports
    @TestMixin(GrailsUnitTestMixin)
    class SomeClassTests {
        @Before
        void setUp() {
            Holders.grailsApplication = grailsApplication
            defineBeans {
                myService(MyServiceMock)
            }
        }
        ....
    }
    
    class MyServiceMock extends MyService {
      // overriden methods here
    }
    

    【讨论】: