Reputation: 43
I'm very new in spock framework testing and I didn't find any example where I can find needed information. Therefore, I think the best way is to show an example what I need to have.
e.g. test class in spock:
def "getData" (){ // this is test of getData method from ExternalService
when:
Result result = externalService.getData()
then:
result.msg = 'SUCCESS'
}
Service Class:
public class ExternalService(){
private ServiceConnector serviceConnector;
public Result getData(){
Result result = serviceConnector.callAndGet();
prepareInformation(data);
updateStatuses(data);
return result;
}
}
Class Data as a Domain Class:
public class Data {
private String msg
private int Id
// +getters/setters
}
And now I have getData
test and would like to mock the only method callAndGet()
. It means every time I call callAndGet
I need to have object Data with msg SUCCESS but all other methods from getData
method should be called normally.
Is it quite understandable? The question is how can we inject/mock the service class ExternalService
into the spock test class?
Upvotes: 2
Views: 4461
Reputation: 11022
You shouldn't try to mock "only one method" of your services. It's just a sign of a bad design, your code is not testable. You should better isolate the dependencies of your class with small services and mock this services in an unit test. Then test the upper layer with an integration test and a complete implementation of your dependencies.
In your example, ServiceConnector
should be an interface, which can be easily mocked. With this condition, the test can be written as :
def "test a mocked service connector"() {
given: "a service connector"
def serviceConnector = Mock(ServiceConnector)
and: "an external service"
def externalService = new ExternalService(serviceConnector:serviceConnector)
when: "Data is loaded"
def result = externalService.data
then: "ServiceConnector is called"
serviceConnector.callAndGet() >> new Result(msg:"SUCCESS")
and: "Data is mocked"
result.msg == "SUCCESS"
}
However, if ServiceConnector is a class and you can't change this, you can use a Partial Mock in Spock. This kind of test are hard to maintain, and can have a lot of side effect :
def "test a mocked service connector"() {
given: "a service connector"
def serviceConnector = Spy(ServiceConnector) {
callAndGet() >> new Result(msg:"SUCCESS")
}
and: "an external service"
def externalService = new ExternalService(serviceConnector:serviceConnector)
when: "Data is loaded"
def result = externalService.data
then: "Data is mocked"
result.msg == "SUCCESS"
}
Upvotes: 3
Reputation: 84786
What you need to do is to mock the ServiceConnector
class and pass it via constructor (e.g.). See below:
@Grab('org.spockframework:spock-core:1.0-groovy-2.4')
@Grab('cglib:cglib-nodep:3.1')
import spock.lang.*
class Test extends Specification {
def 'some spec'() {
given:
def serviceConnector = Mock(ServiceConnector) {
callAndGet() >> new Result(msg: 'SUCCESS')
}
def externalService = new ExternalService(serviceConnector)
when:
Result result = externalService.getData()
then:
result.msg == 'SUCCESS'
}
}
public class ExternalService {
private ServiceConnector serviceConnector
public ExternalService(ServiceConnector serviceConnector) {
this.serviceConnector = serviceConnector
}
public Result getData() {
Result result = serviceConnector.callAndGet()
prepareInformation(result)
updateStatuses(result)
result
}
private void prepareInformation(Result data) {
}
private void updateStatuses(Result data) {
}
}
public class ServiceConnector {
public Result callAndGet() {
}
}
public class Result {
String msg
}
Upvotes: 7