【问题标题】:How to mock autowired beans in a spock test meant for a service?如何在用于服务的 spock 测试中模拟自动装配的 bean?
【发布时间】:2015-09-17 18:54:33
【问题描述】:

我需要将 bean 注入服务以使测试正常工作。我有这个服务类:

class ContentService {

    @Autowired
    Evaluator evaluator

    ... 

}

当运行它的单元测试对应项时,它会失败,说它找不到与Evaluator bean 的良好候选匹配的 bean。我的理解是 Grails 设法为您自动实例化服务变量,但在这里似乎缺少一些信息来正确构建它。那将是测试:

@TestFor(ContentService)
class ContentServiceSpec extends Specification {

    def setup() {
        evaluator = Mock(Evaluator)
        service.evaluator = evaluator
    }

    def "Should test..."() {
       ...
    }

}

模拟experimentEvaluator 并在setup() 方法中手动将其注入测试似乎为时已晚,因为它失败了:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [ExperimentEvaluator] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1301)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1047)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:942)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:533)
    ... 28 more

我已经看到了一些对defineBeans 的引用,尽管它似乎在 2.5.0 中不再使用。我无法理解如何使用它,因为 Grails 测试文档和 API 文档中都没有解释它。

我也见过doWithSpring,但同样的故事:我不知道如何使用它来超越正常使用,而我需要为它创建一个模拟。

我使用 Grails 2.5.0。我是 Grails 的新手,我迷失在它的一些魔力中。

【问题讨论】:

  • EvaluatorExperimentEvaluator 长什么样子?
  • 抱歉,我重新编辑了答案,但 Evaluator 和 ExperimentEvaluator 是相同的。此代码来自专有代码库,因此我不能简单地将其转储在这里。 Evaluator 类是 Java 并且没有空的构造函数。实例化并不容易,模拟它并将其注入服务会更容易(+本着单元测试的精神:))。

标签: spring unit-testing grails spock


【解决方案1】:

您应该使用 doWithSpring 通过 org.codehaus.groovy.grails.commons.InstanceFactoryBean 添加您的依赖项的模拟实例。

import org.codehaus.groovy.grails.commons.InstanceFactoryBean

@TestFor(ContentService)
class ContentServiceSpec extends Specification {

    static doWithSpring = {
        evaluator(InstanceFactoryBean, Mock(Evaluator), Evaluator)
    }



    def "Should test..."() {
       ...
    }

}

【讨论】:

    【解决方案2】:

    您是否尝试过在规范中使用doWithSpring,如下所示?

    import grails.test.mixin.support.GrailsUnitTestMixin
    
    @TestMixin(GrailsUnitTestMixin)
    @TestFor(ContentService)
    class ContentServiceSpec extends Specification {
    
        static doWithSpring = {
            evaluator(Evaluator)
        }
    
        def setup() {
            service.evaluator = evaluator
        }
    
        def "Should test..."() {
           ...
        }
    
    }
    

    请参阅doWithSpring and doWithConfig callback methods, FreshRuntime annotation 了解详细信息(original link 不再有效;新链接指的是 Grails 3.x,但仍与 dowithspring 表示法相关)。

    【讨论】:

    • 感谢您的回复。是的,我已经尝试过了,但问题是 a) Spring 没有 Evaluator bean 可以在任何地方找到,但即便如此,b) 我想模拟 Evaluator。如何模拟 Evaluator 并告诉 Spring 在 Spock 单元测试实例化的服务中使用它?
    • 如果Evaluator 不是一个bean 那么@Autowired 首先在ContentService 中是怎么回事
    • 它必须是在某处声明的 bean,但我找不到它的定义位置。我这样认为的原因是它已经在另一个类/测试中使用了,并且它们在测试和运行时都可以顺利工作。另一个类虽然不是服务,因此不使用@TestFor 注释以及service 对象的spock 单元测试自动实例化。如果我将 @TestFor 替换为 @TestMixin 并手动实例化服务并注入 bean,则效果很好。所以这是我暂时的工作。
    • 如果您能在 github 中提供一个示例应用程序来复制此问题,我将很乐意为您提供帮助。
    • 感谢@dmahapatro 对此进行调查。由于我找到了一种解决方法(即删除 @TestFor 注释并自己管理依赖项注入),如果我在 github 中构建示例应用程序,将花费太多时间来解决这个问题。随着时间的推移,我也会对我使用的现有应用程序更加熟悉,所以也许我稍后会重新审视我的问题。再次感谢您的宝贵时间!
    猜你喜欢
    • 1970-01-01
    • 2020-12-01
    • 2023-03-31
    • 2023-03-23
    • 2015-02-19
    • 1970-01-01
    • 1970-01-01
    • 2016-01-22
    • 1970-01-01
    相关资源
    最近更新 更多