Grails 3: класс Custom 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

person rainerfrey    schedule 15.04.2015    source источник


Ответы (2)


К сожалению, создать класс, расширяющий GrailsAutoConfiguration, недостаточно. Для класса Grails Application по умолчанию некоторые преобразования 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-компоненты, которые вам могут понадобиться. Классы автоконфигурации обычно применяются на основе вашего пути к классам и того, какие bean-компоненты вы определили. Например, если у вас есть tomcat-embedded.jar в вашем пути к классам, вам, вероятно, понадобится TomcatEmbeddedServletContainerFactory (если вы не определили свой собственный bean-компонент EmbeddedServletContainerFactory).

Короткий ответ

Добавьте недостающие аннотации, чтобы ваш класс TestApplication отражал класс выше.

Длинный ответ

Приложения Grails 3 по своей сути являются приложениями Spring Boot. Аналогично, основной метод, предоставляемый классом Grails Application по умолчанию, отвечает за запуск приложения, что он и делает, вызывая GrailsApp.run(). GrailsApp расширяет SpringApplication, который отвечает за тяжелую работу по запуску приложения Spring Boot.

Частью того, за что отвечает SpringApplication, является создание контекста приложения Spring. По умолчанию Spring Boot создает файл AnnotationConfigEmbeddedWebApplicationContext. Как указано в Spring Загрузочная документация:

Этот контекст создаст, инициализирует и запустит EmbeddedServletContainer путем поиска одного bean-компонента EmbeddedServletContainerFactory внутри самого 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
}
person dpcasady    schedule 20.10.2016

иногда проблема вызывалась запуском Grails run-app на другой консоли. когда я запустил тестовое приложение Grails после выключения сервера. Запустилось без ошибок.

person Kishore Vignesh    schedule 22.12.2015