1

I use Spock to test Java-code. For example, I have the following Java-class:

public class Github implements RemoteRepository {
    @Override
    public Boolean isBranchExist(String branch) {
        return new HttpRequest().post();
    }
}

The HttpRequest class itself is also my Java-class. I am trying to mock the creation of an instance of the HttpRequest class.

For this I have the following Groovy unit test:

def "mocking HttpRequest constructor"() {
     given:
     RemoteRepository repository = new Github()
    
     def request = Mock(HttpRequest)
     GroovyMock(HttpRequest, global: true)
    
     new HttpRequest() >> request
    
     and:
     request.post() >> Boolean.FALSE
    
     when:
     def res = repository.isBranchExist("branch_exist")
    
     then:
     res == false
 }

Mocking does not occur, the test fails. However, if I rewrite all my entities (Github, HttpRequest, RemoteRepository) in Groovy, then mocking works successfully and the test passes.

I use gradle as a build tool, here is my gradle.build, here you can also see the dependency versions + I use Oracle OpenJDK 23.0.1:

plugins {
    id 'groovy'
}

dependencies {
    implementation 'org.apache.groovy:groovy-all:4.0.27'
    implementation 'net.bytebuddy:byte-buddy:1.17.5'
    testImplementation 'org.spockframework:spock-core:2.4-M6-groovy-4.0'
}

tasks.named('test') {
    useJUnitPlatform()
}

P.s. I understand that I can redesign the architecture, pass an instance of HttpClientBuilder to the Github constructor, which will provide the ability to send http requests (as is done in the Apache HttpComponents library) and not create HttpRequest objects directly, but I am interested in this particular case, why it works in Groovy code, but does not work in Java code.

The code in this case is greatly simplified, is not actually applicable, just a silly example

2
  • 1
    what exactly do you mean "mocking constructors"? Commented Jun 5 at 11:45
  • 6
    As explained by the Spock docs, a Groovy mock behaves like a regular mock when applied to a non-Groovy class. I.e., like this you cannot mock a constructor call. Simply make the dependency injectable instead of creating it inside the method. Whenever it is difficult to test your code, the code needs to be refactored, not the test. Commented Jun 5 at 12:19

1 Answer 1

0

First, refactor your code to inject the HttpRequest instead of calling new:

public class Github implements RemoteRepository {
    private final HttpRequest httpRequest;

    public GitHub(HttpRequest httpRequest) {
        this.httpRequest = httpRequest;
    }

    @Override
    public Boolean isBranchExist(String branch) {
        return httpRequest.post();
    }
}

Then you can use Spock like this:

def "mocking HttpRequest"() {
     given:
     def request = Mock(HttpRequest) {
        post() >> Boolean.FALSE
     }
     RemoteRepository repository = new Github(request)
        
     when:
     def res = repository.isBranchExist("branch_exist")
    
     then:
     res == false
 }
Sign up to request clarification or add additional context in comments.

1 Comment

Yes, this is what I meant in my comment 3+ months ago. BTW, in the Spock spec a simple expect: and !repository.isBranchExist("branch_exist") instead of when: ... then: ... would be more elegant and readable.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.