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
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With