Spock - mocking external service - groovy

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?

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
}

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"
}

Related

Unable to mock local objects in method using Spock

I'm unable to mock the below local objects - env, service, creds. These are classes from imported cloud foundry dependencies.
How do I write a test case covering all conditions for below Groovy code using Spock or Junit 4 without refactoring the code?
import io.pivotal.cfenv.core.cfEnv
import io.pivotal.cfenv.core.cfCredentials
import io.pivotal.cfenv.core.cfService
class Test {
public String getPropertyValue() {
CfEnv env = new CfEnv();
CfService service = new CfService();
String propName = "test-name";
try {
service = env.findServiceByName(propName);
} catch (Exception e) {
return null;
}
CfCredentials creds = new CfCredentials();
Map<String, Object> props = service.getMap();
return props.get("prop.name").toString();
}
}
As your code is Groovy, you are able to use Spock's GroovySpy
see the docs
For example:
class ASpec extends Specification {
def "getPropertyValue() return null when env.findServiceByName throws an exception"() {
given:
CfEnv envMock = GroovySpy(global: true)
when:
def result = new Test().getPropertyValue()
then:
result == null
1 * envMock.findServiceByName(_) >> { throw new RuntimeException() }
}

In Spock mocks impossible to verify mock against result: MissingPropertyException: No such property

I have some strange error when trying to check mock in then block with the result returned by the tested method, which I don't know beforehand. Here is a simple example:
import spock.lang.Specification
class MockingTest extends Specification {
private MyListener listener = Mock()
def 'test listener called'() {
when:
def service = new MyService(listener)
def message = service.foo()
then:
1 * listener.onEvent(message)
}
class MyService {
private MyListener listener;
MyService(MyListener listener) {
this.listener = listener
}
String foo() {
String message = "Hello " + new Random().nextInt(10);
listener.onEvent(message)
return message;
}
}
class MyListener {
void onEvent(String message) {
System.out.println(message);
}
}
}
And the error is:
No such property: message for class: MockingTest
groovy.lang.MissingPropertyException: No such property: message for class: MockingTest
at MockingTest.test listener called(MockingTest.groovy:14)
Event that
1 * listener.onEvent(message)
is placed in then block seems that Spock tries to initialize it early, even before when block is executed.
Is it any way to work around it, and check that mock is called with some local variable, not a constant?
The thing which is very simple to do with java + mockito appears to be very complex with Spock :(
You can use capture technique for this purpose, where you capture the first argument (it[0]) of the onEvent method call and assign it into pre-declared variable (capturedMessage):
def 'test listener called'() {
given:
def service = new MyService(listener)
String capturedMessage = null
when:
def message = service.foo()
then:
1 * listener.onEvent(_) >> { capturedMessage = it[0] as String }
message == capturedMessage
}
The problem in your example is that the interaction test (1 * listener.onEvent(message)) is executed before the foo() call is finished and then the message variable is not declared yet.
Side note: given is the right section for declaring and initialization of test data like the service.

Implicit assert statement in Groovy

Given I have JUnit tests written in Groovy:
class AssertTests {
#Test
void "explicit assert statement"() {
def value = 42
assert value == 100
}
#Test
void "no assert statement"() {
def value = 42
value == 100
}
}
When I execute them,
explicit assert statement test fails as expected thanks to assert statement.
no assert statement test passes and I would expect it to fail in a similar way how it's done when I use http://spockframework.org
How can I achieve implicit assert behavior for tests written in plain Groovy?
The answer is simple - you can't. Spock uses AST transformations to grab the code written in the then: part and transform it into the code that makes an equivalent of assertion (not the exact assert.)
To illustrate this, here is your test written in Spock:
import spock.lang.Specification
class TestSpec extends Specification {
def "should fail"() {
when:
def value = 42
then:
assert value == 100
}
}
And here is what its bytecode decompiled back to Java looks like:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.GroovyObject;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
import org.spockframework.runtime.ErrorCollector;
import org.spockframework.runtime.SpockRuntime;
import org.spockframework.runtime.ValueRecorder;
import org.spockframework.runtime.model.BlockKind;
import org.spockframework.runtime.model.BlockMetadata;
import org.spockframework.runtime.model.FeatureMetadata;
import org.spockframework.runtime.model.SpecMetadata;
import spock.lang.Specification;
#SpecMetadata(
filename = "TestSpec.groovy",
line = 5
)
public class TestSpec extends Specification implements GroovyObject {
public TestSpec() {
CallSite[] var1 = $getCallSiteArray();
super();
}
#FeatureMetadata(
line = 7,
name = "should fail",
ordinal = 0,
blocks = {#BlockMetadata(
kind = BlockKind.WHEN,
texts = {}
), #BlockMetadata(
kind = BlockKind.THEN,
texts = {}
)},
parameterNames = {}
)
public void $spock_feature_0_0() {
CallSite[] var1 = $getCallSiteArray();
ErrorCollector $spock_errorCollector = (ErrorCollector)ScriptBytecodeAdapter.castToType(var1[0].callConstructor(ErrorCollector.class, false), ErrorCollector.class);
ValueRecorder $spock_valueRecorder = (ValueRecorder)ScriptBytecodeAdapter.castToType(var1[1].callConstructor(ValueRecorder.class), ValueRecorder.class);
Object var10000;
try {
Object value = 42;
try {
SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), "value == 100", Integer.valueOf(12), Integer.valueOf(16), (Object)null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(2)), ScriptBytecodeAdapter.compareEqual($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(0)), value), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(1)), 100))));
var10000 = null;
} catch (Throwable var14) {
SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, "value == 100", Integer.valueOf(12), Integer.valueOf(16), (Object)null, var14);
var10000 = null;
} finally {
;
}
var1[2].call(var1[3].call(this.getSpecificationContext()));
} finally {
$spock_errorCollector.validateCollectedErrors();
var10000 = null;
}
}
}
If you look at the SpockRuntime class, you will find that verifyCondition method checks if the condition found in the then: or and: block evaluates to true:
public static void verifyCondition(#Nullable ErrorCollector errorCollector, #Nullable ValueRecorder recorder,
#Nullable String text, int line, int column, #Nullable Object message, #Nullable Object condition) {
if (!GroovyRuntimeUtil.isTruthy(condition)) {
final ConditionNotSatisfiedError conditionNotSatisfiedError = new ConditionNotSatisfiedError(
new Condition(getValues(recorder), text, TextPosition.create(line, column), messageToString(message), null, null));
errorCollector.collectOrThrow(conditionNotSatisfiedError);
}
}
You can't avoid explicit assert in JUnit tests. You can hide them in some helper method, but you still need to call them. Keep in mind, that JUnit runner requires that the test method returns void, so you can't capture the result of a test method. (Replace void with def, and you will see that JUnit does not run your test.)
If you want to explore the world of compile-time metaprogramming in Groovy, you could experiment with writing your own AST transformations. Maybe there is a way to find boolean expression(s) and inject assert in front of it, but I can't guarantee you that it will work. If you look for an out-of-the-box solution for implicit asserts in Groovy JUnit tests, there is no such one.

Verifying non spy method calls with Spock

I want to use spock to find if a method in a class was called. But when I try to verify it, the when block says that the method was never called.
public class Bill {
public Bill(Integer amount) {
this.amount = amount;
}
public void pay(PaymentMethod method) {
Integer finalAmount = amount + calculateTaxes();
method.debit(finalAmount);
}
private Integer calculateTaxes() {
return 0;
}
private final Integer amount;
}
public class PaymentMethod {
public void debit(Integer amount) {
// TODO
}
public void credit(Integer amount) {
// TODO
}
}
import spock.lang.Specification
import spock.lang.Subject
class BillSpec extends Specification {
#Subject
def bill = new Bill(100)
def "Test if charge calculated"() {
given:
PaymentMethod method = Mock()
when:
bill.pay(method)
then:
1 * method.debit(100)
// 1 * bill.calculateTaxes() // Fails with (0 invocations) error
0 * _
}
}
In the example above all I want to do is verify if calculateTaxes is being called but the test fails with (0 invocations). I tried using spy but am not sure what would be the syntax since Bill takes a parameterized constructor.
You can test calculateTaxes() call when you Spy the Bill instance like this:
class SpyTestSpec extends Specification {
def "Test if charge calculated"() {
given:
def bill = Spy(new Bill(100))
PaymentMethod method = Mock()
when:
bill.pay(method)
then:
1 * method.debit(100)
1 * bill.calculateTaxes()
1 * bill.pay(method)
0 * _
}
}
Another important thing is to make calculateTaxes() method visible for the test, otherwise it will still fail:
public Integer calculateTaxes() { ... }
Note that if you want to test that nothing else was called then you should also add:
1 * bill.pay(method)
And here is the result:

Unit test Groovy2.0 with Spock : setup( )

I am writing unit test using Spock for groovy-2.0 , and using gradle to run. If I write following the test pass.
import spock.lang.Specification
class MyTest extends Specification {
def "test if myMethod returns true"() {
expect:
Result == true;
where:
Result = new DSLValidator().myMethod()
}
}
myMethod() is a simple method in DSLValidator class, that simply returns true.
But if I write a setup() function and create the object in setup(), my test fails: Gradel says: FAILED: java.lang.NullPointerException: Cannot invoke method myMethod() on null object
Following is what it looks like with setup(),
import spock.lang.Specification
class MyTest extends Specification {
def obj
def setup(){
obj = new DSLValidator()
}
def "test if myMethod returns true"() {
expect:
Result == true;
where:
Result = obj.myMethod()
}
}
Can somebody help?
Here is the solution I got to the problem:
import spock.lang.Specification
class DSLValidatorTest extends Specification {
def validator
def setup() {
validator = new DSLValidator()
}
def "test if DSL is valid"() {
expect:
true == validator.isValid()
}
}
In Spock objects stored into instance fields are not shared between feature methods. Instead, every feature method gets its own object.
If you need to share an object between feature methods, declare a #Shared field.
class MyTest extends Specification {
#Shared obj = new DSLValidator()
def "test if myMethod returns true"() {
expect:
Result == true
where:
Result = obj.myMethod()
}
}
class MyTest extends Specification {
#Shared obj
def setupSpec() {
obj = new DSLValidator()
}
def "test if myMethod returns true"() {
expect:
Result == true
where:
Result = obj.myMethod()
}
}
There are 2 fixture methods for setting up the environment:
def setup() {} // run before every feature method
def setupSpec() {} // run before the first feature method
I don't understand why the second example with setupSpec() works and fails with setup() because in documentation says otherwise:
Note: The setupSpec() and cleanupSpec() methods may not reference
instance fields.

Resources