4

I wnat to test my Java application with Spock. I have a class AmiPlugin.java that has two dependencies via constructor injection:

@Autowired
AmiPlugin(final EC2InstanceContextProvider contextProvider,
                         final ViolationSink violationSink) {
     super(contextProvider);
     this.violationSink = violationSink;
}

My groovy test looks like this:

@ContextConfiguration(loader = SpringApplicationContextLoader   , classes = [AmiPlugin.class])
class AmiPluginSpec extends Specification{

@Autowired
AmiPlugin amiPlugin;

def "Test if plugin supports the right Cloudtrail events"(String event, Boolean supports){
    expect:
    amiPlugin.supportsEventName().test(event) == supports

    where:
    event                   | supports
    "RunInstances"          | true
    "StartInstances"        | false
    "TerminateInstances"    | false
    "StopInstances"         | false
    "Foobar"                | false
}

However, running this gives me an NoSuchBeanDefinitionException. In jUnit, I would have used a static class with @configuration in the same file, that returns the needed mocks, but that isn't working. Using

    @Bean
    EC2InstanceContextProvider ec2InstanceContextProviderMock(){
        EC2InstanceContextProvider provider = Mock()
        return provider
    }

is also not working.

How can I autowire mocks in Spock?

EDIT Sure thing

java.lang.IllegalStateException: Failed to load ApplicationContext

    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
    at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83)
    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:228)
    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.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'amiPlugin': Unsatisfied dependency expressed through constructor argument with index 0 of type [org.zalando.stups.fullstop.plugin.EC2InstanceContextProvider]: No qualifying bean of type [org.zalando.stups.fullstop.plugin.EC2InstanceContextProvider] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.zalando.stups.fullstop.plugin.EC2InstanceContextProvider] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:749)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:185)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1143)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1046)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:839)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:766)
    at org.springframework.boot.SpringApplication.createAndRefreshContext(SpringApplication.java:361)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:307)
    at org.springframework.boot.test.SpringApplicationContextLoader.loadContext(SpringApplicationContextLoader.java:98)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116)
    ... 13 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.zalando.stups.fullstop.plugin.EC2InstanceContextProvider] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1373)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1119)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:813)
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741)
    ... 31 more
Gregsen
  • 471
  • 1
  • 5
  • 15

2 Answers2

3

What you're looking for is detached mocks that let you create mocks out of specification. https://github.com/spockframework/spock/issues/86 - Ability to inject Spock mocks as Spring beans. There you can find a hack how to do it.

UPDATE 2017-03-25: Full support of detached mocks was added to Spock in version 1.1. Usage examples can be found in the documentation and in this SO answer.

Community
  • 1
  • 1
Alex K.
  • 714
  • 4
  • 14
2

You can use SpringBean annotation with Spock 1.2 : https://objectpartners.com/2018/06/14/spock-1-2-annotations-for-spring-integration-testing/

So your code would become :

@ContextConfiguration(loader = SpringApplicationContextLoader   , classes = [AmiPlugin.class])
class AmiPluginSpec extends Specification{

@SpringBean
AmiPlugin amiPlugin = Mock() 

def "Test if plugin supports the right Cloudtrail events"(String event, Boolean supports){
    expect:
    amiPlugin.supportsEventName().test(event) == supports

    where:
    event                   | supports
    "RunInstances"          | true
    "StartInstances"        | false
    "TerminateInstances"    | false
    "StopInstances"         | false
    "Foobar"                | false
}

You don't need anymore additional configuration class because the mock is injected directly by Spock into spring context

  • It's the way to add mock in the spring context so I don't understand why it wouldn't be a correct answer – Vincent Couturier Jun 29 '18 at 06:57
  • For clarification @VincentCouturier : It is a correct answer if you post a link. Is it a good answer? Rather not. If the content of the link is changed or the website goes down, the answer becomes invalid as there is no more content. Hence it is better to include the content in your answer with the link as reference/source as you did now. I retracted my flag and removed my comment :) – geisterfurz007 Jun 29 '18 at 08:26