Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Autowire mock with Spock in Spring Application

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
like image 231
Gregsen Avatar asked Sep 03 '25 16:09

Gregsen


2 Answers

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.

like image 119
Alex K. Avatar answered Sep 05 '25 04:09

Alex K.


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

like image 21
Vincent Couturier Avatar answered Sep 05 '25 04:09

Vincent Couturier