A custom spock annotation to handle both applying and cleaning up metaclass changes? - groovy

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.

Related

How to reuse feature methods in a #Stepwise Spock specification?

I have two Spock specifications, both test a long procedure from a different starting point, so it's like
#Stepwise
class FooSpec extends Specification {
def "setup1"() {...}
def "setup2"() {...}
def "common1"() {...}
def "common2"() {...}
...
}
#Stepwise
class BarSpec extends Specification {
def "setup3"() {...}
def "setup4"() {...}
def "common1"() {...}
def "common2"() {...}
...
}
Now I'd like to refactor my code to deduplicate all the common* feature methods, that are to be executed after the different setups.
I tried to use subclassing, but the superclass' feature methods are executed before and not after the subclass' ones. I also tried to write an own Spock extension (Spock version 0.7 for Groovy 2), but couldn't find a way to implement my desired behaviour there.
Consulting the source code of StepwiseExtension, I finally managed to come up with my own solution:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#ExtensionAnnotation(BeforeSuperExtension)
#interface BeforeSuper {}
This annotation marks feature methods in a #Stepwise test, that should be executed before super's feature methods.
The extension implementation will rearrange the execution order accordingly:
class BeforeSuperExtension extends AbstractAnnotationDrivenExtension<BeforeSuper>
{
def beforeSuper = []
#Override
void visitFeatureAnnotation(BeforeSuper annotation, FeatureInfo feature)
{
beforeSuper << feature
}
#Override
void visitSpec(SpecInfo spec)
{
def superFeatures = spec.superSpec.allFeaturesInExecutionOrder
def afterSuper = spec.features - beforeSuper
(beforeSuper + superFeatures + afterSuper).eachWithIndex { f, i ->
f.executionOrder = i
}
}
}
Put the common* methods in a base class, and add two subclasses with a setupSpec() method (instead of the setup* methods).
I've implemented the opposite strategy, with an annotation #AfterSubSpec that tells common methods in the superclass to run after methods in the subclass:
class AfterSubSpecExtension extends AbstractAnnotationDrivenExtension<AfterSubSpec> {
def afterSub = []
#Override
void visitFeatureAnnotation(AfterSubSpec annotation, FeatureInfo feature) {
afterSub << feature
}
#Override
void visitSpec(SpecInfo spec) {
def subSpecFeatures = spec.bottomSpec.allFeaturesInExecutionOrder - afterSub
(subSpecFeatures + afterSub).eachWithIndex { f, i -> f.executionOrder = i }
}
}

Groovy get current methods annotation

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

Load groovy classes/scripts dynamically without compilation?

I have a set of groovy scripts in package hierarchy. I have 1 main script, from which I want to call others. For example I have these scripts (with public classes/interfaces of the same name in them):
package.MainScript
package.MyInterface;
package.utils.MyInterfaceImpl1 //implements MyInterface
package.utils.MyInterfaceImpl2 //implements MyInterface
Is there a way to call one script from the other without knowing called class name at compile time? I mean to do something like dynamic class loading like:
class MainScript {
public static void main (String[] args) {
MyInterface instance = Class.forName("package.utils.Util1");
}
}
Yeah! Groovy is a dynamic language. You can create class instance dynamically.
package.MyInterface
class MyInterfaceImpl1 {
def greet() {
"Hello"
}
}
package.MyInterface
class MyInterfaceImpl2 {
def greet() {
"Hi!"
}
}
def name = 'MyInterfaceImpl1' // Choose whatever you want at runtime
def className = Class.forName("MyInterface.$name")
def instance = className.newInstance()
assert instance.greet() == 'Hello'

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.

ExpandoMetaClass - Static Methods + singleton + overloaded functions

Using ExpandoMetaClass Static Methods can be added dynamically, how can i use this ExpandoMetaClass in Singleton object, with overloaded static function in it, let say the sample program need to be re written using ExpandoMetaClass whats needs to changed in the below program
#Singleton
class testA {
def static zMap = [:]
static def X() {
Y()
}
static def Y() {
}
static def X(def var) {
Y(var)
}
static def Y(def var) {
zMap.put(var)
}
}
One of the reasons to use a singleton is to avoid having static state and methods in a class. If you're using #Singleton, there's no reason to have static methods or fields. The way to use a singleton is like this:
#Singleton class TestA {
def someField = "hello"
def methodX() {
someField
}
}
println TestA.instance.methodX()
You can extend the singleton using ExpandoMetaClass like so:
TestA.instance.metaClass.newMethod = { -> "foo" }
TestA.instance.metaClass.methodX = { -> "goodbye" }
println TestA.instance.newMethod()
println TestA.instance.methodX()
If you really want a static method, you can do something like this:
TestA.metaClass.static.methodY = { -> "I am static" }
println TestA.methodY()
Note that if you override the class metaClass, rather than the instance metaClass, it won't apply to the instance if the instance has already been created. To get around this use #Singleton(lazy = true) and override the metaClass before accessing the instance.

Resources