Grails 3:用于集成测试的自定义应用程序类



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

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测试应用程序或gradle integrationTest)会导致ApplicationContextException,其根本原因是缺少EmbeddedServlet ContainerFactory。这是一个错误,还是我错误地使用了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

不幸的是,创建一个扩展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应用程序上下文的自动配置,尝试猜测和配置您可能需要的bean。自动配置类通常基于类路径应用以及你定义了什么豆子。例如,如果您tomcat-embedded.jarTomcatEmbeddedServletContainerFactory(除非您已定义自己的EmbeddedServletContainerFactorybean)。

简短回答

添加缺少的注释,以便TestApplication类镜像上面的注释。

答案很长

Grails 3应用程序的核心是Spring Boot应用程序。同样,默认GrailsApplication类公开的主要方法负责运行应用程序,它通过调用GrailsApp.run()来执行此操作。GrailsApp扩展了SpringApplication,后者负责运行Spring Boot应用程序的繁重工作。

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

此上下文将创建、初始化并运行EmbeddedServletContainer通过搜索单个ApplicationContext中的EmbeddedServletContainerFactorybean它本身

因此,很明显,为了实现这一点,需要在某个地方定义一个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
}

有时在另一个控制台上运行Grails运行应用程序会导致问题。当我关闭服务器后运行grails测试应用程序时。它运行时没有出现错误。

最新更新