Mocks and Guice

74 views
Skip to first unread message

Andres Almiray

unread,
Oct 23, 2017, 12:13:36 PM10/23/17
to Spock Framework - User
Hello everyone,

I'm exploring Spock's options for binding mocks with Guice given spock-guice. I've encountered a few problems and would like to get your advice on the matter.

The CUT looks like this

public class AppController {
   
@Inject private AppModel model;
   
@Inject private Github github;
   
@Inject private ApplicationEventBus eventBus;

   
public void loadRepositories() {
        model
.setState(RUNNING);
        github
.repositories(model.getOrganization())
           
.done(model.getRepositories()::addAll)
           
.fail(throwable -> eventBus.publishAsync(new ThrowableEvent(throwable)))
           
.always((state, resolved, rejected) -> model.setState(READY));
   
}
}

The `Github` type is the one I'm interested in mocking out. My first attempt to mock this type and define an interaction for it is shown next

@UseModules(TestModule)
class AppControllerSpec extends Specification {
   
private static final String ORGANIZATION = 'foo'

   
@Inject private AppController controller
   
@Inject private AppModel model
   
@Inject private ApplicationEventBus eventBus
   
@Inject private Github github

   
def happyPath() {
        given
:
       
Collection<Repository> repositories = TestHelper.createSampleRepositories()
       
Promise<Collection<Repository>, Throwable, Void> promise = new DeferredObject<Collection<Repository>, Throwable, Void>().resolve(repositories)
        github
.repositories(ORGANIZATION) >> promise

       
when:
        model
.organization = ORGANIZATION
        controller
.loadRepositories()

       
then:
        model
.repositories.size() == 3
        model
.repositories == repositories

       
1 * github.repositories(ORGANIZATION)
   
}

   
static class TestModule extends AppModule {
       
private final MockFactory mockFactory = new DetachedMockFactory()

       
@Override
       
protected void bindGithub() {
            bind
(Github).toInstance(mockFactory.Mock(Github))
       
}
   
}
}

Unfortunately the `AppController` class complains with an NPE as `grihub.repositories('foo')` returns  a `null` as a default empty response. This means the interaction was ignored. Next I tried an explicit Mock in place but that caused the same NPE

@UseModules(AppModule)
class AppControllerSpec extends Specification {
   
private static final String ORGANIZATION = 'foo'

   
@Inject private AppController controller
   
@Inject private AppModel model
   
@Inject private ApplicationEventBus eventBus

   
def happyPath() {
        given
:
       
Collection<Repository> repositories = TestHelper.createSampleRepositories()
       
Promise<Collection<Repository>, Throwable, Void> promise = new DeferredObject<Collection<Repository>, Throwable, Void>().resolve(repositories)
        controller
.@github  = Mock(Github) {
            repositories
(ORGANIZATION) >> promise
       
}

       
when:
        model
.organization = ORGANIZATION
        controller
.loadRepositories()

       
then:
        model
.repositories.size() == 3
        model
.repositories == repositories

       
1 * github.repositories(ORGANIZATION)
   
}
}

Next I turned to making the Mock a simple Stub and got the same problem. Just for kicks started to play around with wildcards and ended up with a working version

@UseModules(AppModule)
class AppControllerSpec extends Specification {
   
private static final String ORGANIZATION = 'foo'

   
@Inject private AppController controller
   
@Inject private AppModel model
   
@Inject private ApplicationEventBus eventBus

   
def happyPath() {
        given
:
       
Collection<Repository> repositories = TestHelper.createSampleRepositories()
       
Promise<Collection<Repository>, Throwable, Void> promise = new DeferredObject<Collection<Repository>, Throwable, Void>().resolve(repositories)
        controller
.@github = Stub(Github) {
            _
(ORGANIZATION) >> promise
       
}

       
when:
        model
.organization = ORGANIZATION
        controller
.loadRepositories()

       
then:
        model
.repositories.size() == 3
        model
.repositories == repositories
   
}
}

While it works it kind of defeats the purpose of using a DI container and mocks together, besides the use of a wildcard in this particular case is less than optimal. Any hints to make the code simpler, perhaps making it look like the first snippet, would be appreciated.

Spock 1.1-groovy-2.4
Groovy 2.4.12
Guice 4.1.0

Source code available at https://github.com/aalmiray/javatrove/tree/master/github-api-01.

Thanks!

Andres Almiray

unread,
Oct 23, 2017, 12:14:23 PM10/23/17
to Spock Framework - User
Reply all
Reply to author
Forward
0 new messages