Can Spock Mock a Java constructor

16,541

Solution 1

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.

Solution 2

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
}
Share:
16,541

Related videos on Youtube

JoeG
Author by

JoeG

Updated on June 11, 2022

Comments

  • JoeG
    JoeG almost 2 years

    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?

  • JoeG
    JoeG about 10 years
    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.
  • Peter Niederwieser
    Peter Niederwieser about 10 years
    Why can't you add a second constructor?
  • JoeG
    JoeG about 10 years
    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.
  • Peter Niederwieser
    Peter Niederwieser about 10 years
    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.
  • Beckster
    Beckster about 4 years
    the question asked for a constructor mocking. This is mocking a static method