Can Spock Mock a Java constructor
Asked Answered
M

3

8

Trying to broaden the appeal of Spock at work and run into this issue. Actually trying to write Unit Tests for a Groovy class, but one that calls out to Java. A static method calls a private constructor. The code looks like:

private MyConfigurator(String zkConnectionString){
    solrZkClient = new SolrZkClient(zkConnectionString, 30000, 30000,
            new OnReconnect() {
                @Override
                public void command() { . . . }
            });
}

"SolrZkClient" is from third party (Apache) Java library. Since it tries to connect to ZooKeeper, I would like to mock that out for this Unit Test (rather than running one internally as part of the unit test).

My test gets to the constructor without difficulty, but I can't get past that ctor:

def 'my test'() {
    when:
        MyConfigurator.staticMethodName('hostName:2181')
    then:
        // assertions
}

Is there anyway to do this?

Millepore answered 17/1, 2014 at 19:44 Comment(0)
C
10

Since the class under test is written in Groovy, you should be able to mock the constructor call by way of a global Groovy Mock/Stub/Spy (see Mocking Constructors in the Spock Reference Documentation). However, a better solution is to decouple the implementation of the MyConfigurator class, in order to make it more testable. For example, you could add a second constructor and/or static method that allows to pass an instance of SolrZkClient (or a base interface, if there is one). Then you can easily pass in a mock.

Cowans answered 17/1, 2014 at 20:50 Comment(4)
Thanks Peter, seems like very 'common-sense' advice. Been delaying accepting this answer as putting it into practice has been problematic. Examination of even the small amount of code shows why. The purpose of the class is to be an Abstraction that hides the underlying details of SolrCloud configuration. To 'abstract away' those details, all I need is a connectString. Thus passing in the object the class is trying to hide is not really an option. Will spend more time on this today and I will comment again.Millepore
Why can't you add a second constructor?Cowans
That would break the encapsulation - one of the purposes of the class is to hide the construction of the SolrZkClient, not to pass that up the calling chain.Millepore
You can make the constructor package-private, and add a comment (or a @OnlyForTesting annotation). It's common to slightly open up a class for better testability.Cowans
T
1

You can use GroovySpy for mocking constructors in Spock

For example:

def 'my test'() {
 given: 
 def solrZkClient = GroovySpy(SolrZkClient.class,global: true);
 when:
    MyConfigurator.staticMethodName('hostName:2181')
 then:
    // assertions
}
Transferase answered 14/12, 2018 at 18:46 Comment(1)
the question asked for a constructor mocking. This is mocking a static methodCulex
C
0
def anySubscriber = GroovySpy(RealSubscriber, global: true)

1 * new RealSubscriber("Fred")

put that into def setup(){ } block or inside of given: label block

Culex answered 14/2, 2020 at 18:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.