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.
Related
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.
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"
}
I want to modify the metaclass of a type Bar when I run unit tess using Spock and Groovy.
Currently I do the following:
#ConfineMetaClassChanges(Bar) -- this will cleanup the metaclass changes
class FooSpec extends Specification {
def setupSpec() {
BarHelper.setupMetaClass() -- this applies the metaclass changes
}
def "test Foo"() {
given:
def bar = createBar()
def foo = createFoo(bar.x, bar.y)
...
}
}
class BarHelper {
static def setupMetaClass() {
Bar.metaclass.x = { ... }
Bar.metaclass.y = { ... }
}
}
Having two operations to (a) change the metaclass and (b) clean up the metaclass changes is wasteful. I'm wondering if I can write an annotation that will do both, e.g :
#UsingBar -- internally, calls BarHelper.setupMetaClass(), and
-- also invokes #ConfineMetaClassChanges(Bar)
class FooSpec extends Specification {
-- no need for setupSpec() method now
def "test Foo"() {
given:
def bar = createBar()
def foo = createFoo(bar.x, bar.y)
...
}
}
With JUNit I can write a test rule or a class rule to do something similar, but I'm not sure of the best practice with Spock.
I think I have found a simpler solution: using groovy categories and the Spock #Uses annotation:
class BarCategory {
static getX(Bar bar) { ... }
static getY(Bar bar) { ... }
}
#Uses(BarCategory)
public class FooSpec extends Specification {
def "test Foo"() {
given:
def bar = createBar()
def foo = createFoo(bar.x, bar.y)
...
}
}
This is equivalent to the following:
def "test Foo"() {
given:
uses(BarCategory) {
def bar = createBar()
def foo = createFoo(bar.x, bar.y)
}
...
}
And I don't need to worry about metaclass changes infecting other code.
I have the following two Groovy classes:
Buzz.groovy:
import widgets.Fizz
class Buzz {
def WhistleFeather
def init = { servletContext ->
WhistleFeather.load()
println "WhistleFeather loaded."
}
def destroy = { }
}
WhistleFeather.groovy:
package net.me.myapp
import widgets.Fizz
public class WhistleFeather {
def navMenu
def load() {
println "Loading!"
}
}
When execution gets to the WhistleFeather.load() method call I'm getting a NullPointerException. Can anyone see why?
WhistleFeather#load is an instance method, not a static method. Either call it with new WhistleFeather().load(), or turn it into a static method (static load() { ... }).
I created a java interface for my annotation. I am now writing a geb spock test and I would like to print the annotation values so it shows in the gradle report. Is this possible? Here is my test case and let me know if I am doing this wrong
class Checkout extends GebReportingSpec {
#TestCase(someID="12345")
def "A checkout 3-D script"() {
// My test steps.....
}
}
Use StackTraceUtils.sanitize to get the current method and use reflection to iterate through the annotations:
import java.lang.annotation.*
import org.codehaus.groovy.runtime.StackTraceUtils
class Checkout {
#TestCase(someID="12345")
def "yeah a"() {
printTestCaseId()
// My test steps.....
}
def printTestCaseId() {
def stack = StackTraceUtils.sanitize(new Throwable()).stackTrace[1]
def method = getClass().declaredMethods.find { it.name == stack.methodName }
println method
def someID = method.annotations[0].someID()
println someID
assert someID == "12345"
}
}
#Retention (RetentionPolicy.RUNTIME)
#interface TestCase { String someID() }
co = new Checkout()
co."${'yeah a'}"()
The StackTraceUtils is not needed if you are the one iterating through the methods.
spockframework (version "spock-core-1.1-groovy-2.4") is provides way of accessing annotation:
package com.test.integration.spec
import com.test.integration.annotation.Scenario
import com.test.integration.annotation.TestCase
import spock.lang.Specification
#Scenario("AnnotationSpec")
class AnnotationSpec extends Specification {
String scenario
String test_case
def setup() {
scenario = specificationContext.currentSpec.getAnnotation(Scenario).value()
test_case = specificationContext.currentFeature.featureMethod.getAnnotation(TestCase).value()
}
#TestCase("case-001")
def 'spock provides way of accessing annotation'(){
expect:
"AnnotationSpec" == scenario
"case-001" == test_case
}
}