Skip to content

Commit

Permalink
Improve GuiceExtension auto attaches mocks
Browse files Browse the repository at this point in the history
  • Loading branch information
leonard84 committed Nov 15, 2017
1 parent 28f7c87 commit 9b333fb
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 7 deletions.
2 changes: 2 additions & 0 deletions docs/modules.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ include::include.adoc[]
Integration with the http://code.google.com/p/google-guice/[Guice] IoC container. For examples see the specs in the
https://github.com/spockframework/spock/tree/master/spock-guice/src/test/groovy/org/spockframework/guice[codebase].

With Spock 1.2 detached mocks are automatically attached to the `Specification` if they are injected via `@Inject`.

include::module_spring.adoc[leveloffset=+1]

== Tapestry Module
Expand Down
1 change: 1 addition & 0 deletions docs/release_notes.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Breaking Changes: Spock 1.2 drops support for Java 6, Groovy 2.0 and Groovy 2.3
* Improve support for builder pattern, stubs now return themselves if the return type matches the type of the stub
* Improve tapestry support with by supporting `@ImportModule`
* Improve `constructorArgs` for spies can now accept a map directly without the need to wrap it in a list
* Improve <<modules.adoc#_guice_module,Guice Module>> now automatically attaches detached mocks
* General dependency update

Thanks to all the contributors to this release: Rob Elliot, jochenberger, Jan Papenbrock, Paul King, Marcin Zajączkowski, mrb-twx
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@

package org.spockframework.guice;

import org.spockframework.mock.MockUtil;
import org.spockframework.runtime.extension.*;
import org.spockframework.runtime.model.SpecInfo;
import spock.lang.Shared;
import spock.lang.*;

import java.lang.reflect.Field;
import java.util.*;
Expand All @@ -34,6 +35,7 @@
// Important implementation detail: Only the fixture methods of
// spec.getTopSpec() are intercepted (see GuiceExtension)
public class GuiceInterceptor extends AbstractMethodInterceptor {
private static final MockUtil MOCK_UTIL = new MockUtil();
private final Set<Class<? extends Module>> moduleClasses;
private final Set<InjectionPoint> injectionPoints;

Expand All @@ -47,13 +49,13 @@ public GuiceInterceptor(SpecInfo spec, Set<Class<? extends Module>> moduleClasse
@Override
public void interceptSharedInitializerMethod(IMethodInvocation invocation) throws Throwable {
createInjector();
injectValues(invocation.getSharedInstance(), true);
injectValues(invocation.getSharedInstance(), true, (Specification)invocation.getInstance());
invocation.proceed();
}

@Override
public void interceptInitializerMethod(IMethodInvocation invocation) throws Throwable {
injectValues(invocation.getInstance(), false);
injectValues(invocation.getInstance(), false, (Specification)invocation.getInstance());
invocation.proceed();
}

Expand All @@ -73,7 +75,7 @@ private List<Module> createModules() {
return modules;
}

private void injectValues(Object target, boolean sharedFields) throws IllegalAccessException {
private void injectValues(Object target, boolean sharedFields, Specification specInstance) throws IllegalAccessException {
for (InjectionPoint point : injectionPoints) {
if (!(point.getMember() instanceof Field))
throw new GuiceExtensionException("Method injection is not supported; use field injection instead");
Expand All @@ -82,6 +84,9 @@ private void injectValues(Object target, boolean sharedFields) throws IllegalAcc
if (field.isAnnotationPresent(Shared.class) != sharedFields) continue;

Object value = injector.getInstance(point.getDependencies().get(0).getKey());
if (MOCK_UTIL.isMock(value)) {
MOCK_UTIL.attachMock(value, specInstance);
}
field.setAccessible(true);
field.set(target, value);
}
Expand Down
9 changes: 6 additions & 3 deletions spock-guice/src/main/java/spock/guice/UseModules.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,20 @@

package spock.guice;

import org.spockframework.guice.GuiceExtension;
import org.spockframework.runtime.extension.ExtensionAnnotation;

import java.lang.annotation.*;

import com.google.inject.Module;

import org.spockframework.guice.GuiceExtension;
import org.spockframework.runtime.extension.ExtensionAnnotation;

/**
* Activates <a href="http://code.google.com/p/google-guice/">Guice</a> integration for a specification.
* The specified modules will be started before and stopped after the specification's execution.
* Services will be injected into the specification based on regular Guice annotations.
*
* Detached mocks/stubs created by the {@link spock.mock.DetachedMockFactory} are automatically attached
* to the Specification when they are injected via {@code @Inject}.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.spockframework.guice

import spock.mock.DetachedMockFactory

import com.google.inject.AbstractModule

class MockModule extends AbstractModule {
@Override
protected void configure() {
DetachedMockFactory detachedMockFactory = new DetachedMockFactory()
bind(IService1).toInstance(detachedMockFactory.Mock(IService1))
bind(IService2).toInstance(detachedMockFactory.Stub(IService2))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.spockframework.guice

import spock.guice.UseModules
import spock.lang.Specification

import javax.inject.Inject

@UseModules(MockModule)
class MockSpec extends Specification {

@Inject
IService1 service1

@Inject
IService2 service2

def "mocks and stubs are auto attached on injection" () {
given:
service1.generateString() >> 'hello'
service2.generateQuickBrownFox() >> 'world'

expect:
service1.generateString() == 'hello'
service2.generateQuickBrownFox() == 'world'
}


def "mocking works as well" () {
when:
service1.generateString() == 'hello'

then:
1 * service1.generateString() >> 'hello'
}
}

0 comments on commit 9b333fb

Please sign in to comment.