5

我想自定义用于集成测试的 Application 类。根据用户指南,这应该是可能的:

Integration 注释支持可选的 applicationClass 属性,该属性可用于指定用于功能测试的应用程序类。该类必须扩展 GrailsAutoConfiguration。

(来自http://grails.github.io/grails-doc/3.0.x/guide/testing.html#integrationTesting

所以我的集成测试用

@Integration(applicationClass = TestApplication)
class DataServiceSpec extends Specification {

测试应用程序类(尚未定制)如下所示:

class TestApplication extends GrailsAutoConfiguration {
}

运行集成测试(使用 grails test-app 或 gradle integrationTest 导致 ApplicationContextException,根本原因是缺少 EmbeddedServletContainerFactory。这是一个错误,还是我错误地使用了 applicationClass 属性?这样的自定义应用程序类应该驻留在哪里?我当我将它放在集成测试源和 grails-app/init 中时出现相同的错误。或者是否有另一种方法可以将另一个 @Configuration 类添加到集成测试上下文中?

这是完整的堆栈跟踪:

java.lang.IllegalStateException: Failed to load ApplicationContext
    at org.springframework.test.context.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:94)
    at org.springframework.test.context.DefaultTestContext.getApplicationContext(DefaultTestContext.java:72)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:212)
    at org.spockframework.spring.SpringTestContextManager.prepareTestInstance(SpringTestContextManager.java:49)
    at org.spockframework.spring.SpringInterceptor.interceptSetupMethod(SpringInterceptor.java:42)
    at org.spockframework.runtime.extension.AbstractMethodInterceptor.intercept(AbstractMethodInterceptor.java:28)
    at org.spockframework.runtime.extension.MethodInvocation.proceed(MethodInvocation.java:87)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:86)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:49)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:64)
    at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:50)
    at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
    at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.messaging.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
    at org.gradle.messaging.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:106)
    at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
    at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.messaging.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:360)
    at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.java:64)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.context.ApplicationContextException: Unable to start embedded container; nested exception is org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:133)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:474)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:686)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:320)
    at grails.boot.GrailsApp.run(GrailsApp.groovy:49)
    at org.springframework.boot.test.SpringApplicationContextLoader.loadContext(SpringApplicationContextLoader.java:101)
    at org.springframework.test.context.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:68)
    at org.springframework.test.context.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:86)
    ... 24 more
Caused by: org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.getEmbeddedServletContainerFactory(EmbeddedWebApplicationContext.java:183)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.createEmbeddedServletContainer(EmbeddedWebApplicationContext.java:156)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:130)
    ... 32 more
4

2 回答 2

2

不幸的是,创建一个可扩展的类GrailsAutoConfiguration是不够的。对于默认的 GrailsApplication类,一些 AST 转换发生在幕后,以提供应用程序运行所需的一切。说完了,Application 类看起来更像这样:

@EnableWebMvc
@EnableAutoConfiguration(exclude = [DataSourceAutoConfiguration, MessageSourceAutoConfiguration, ReactorAutoConfiguration])
public class Application extends GrailsAutoConfiguration {
    static void main(String[] args) {
        GrailsApp.run(Application, args)
    }
}

@EnableAutoConfiguration注释是真正使事情起作用的原因。Spring Boot 文档描述了它的作用:

启用 Spring Application Context 的自动配置,尝试猜测和配置您可能需要的 bean。自动配置类通常根据您的类路径和您定义的 bean 应用。例如,如果您的类路径中有 tomcat-embedded.jar,您可能需要一个 TomcatEmbeddedServletContainerFactory(除非您已经定义了自己的EmbeddedServletContainerFactorybean)。

简答

添加缺少的注释,以便您的TestApplication班级反映上述注释。

长答案

Grails 3 应用程序的核心是 Spring Boot 应用程序。同样,默认 GrailsApplication类公开的 main 方法负责运行应用程序,它通过调用GrailsApp.run(). GrailsAppextendsSpringApplication负责运行 Spring Boot 应用程序的繁重工作。

部分SpringApplication责任是创建 Spring 应用程序上下文。默认情况下,Spring Boot 创建一个AnnotationConfigEmbeddedWebApplicationContext. 如Spring Boot 文档中所述:

此上下文将通过在其自身中EmbeddedServletContainer搜索单个 EmbeddedServletContainerFactorybean来创建、初始化和运行 an 。ApplicationContext

所以很明显,为了让它起作用,需要在EmbeddedServletContainerFactory某个地方定义。您看到的异常是由于没有找到。我们在这里有两个选择。您可以执行 Grails 对默认 Application 类所做的操作并添加@EnableAutoConfiguration注释,如上所示,或者显式定义您自己的EmbeddedServletContainerFactory

@Configuration
class TestApplication extends GrailsAutoConfiguration {
    static void main(String[] args) {
        GrailsApp.run(TestApplication, args)
    }

    @Bean
    public EmbeddedServletContainerFactory containerFactory() {
        return new TomcatEmbeddedServletContainerFactory(0)
    }
}

请注意,默认情况下,Grails 仅扫描与 Application 类相关的类。您可能需要通过将以下内容添加到您的Application类来覆盖它:

@Override
protected boolean limitScanningToApplication() {
    return false
}
于 2016-10-20T20:05:36.077 回答
0

有时在另一个控制台上运行 Grails run-app 会导致问题。当我在关闭服务器后运行 grails test-app 时。它运行没有错误。

于 2015-12-22T17:02:17.940 回答