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

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

Related

Mocking a singleton in Groovy

In Groovy / Spock I can mock a class by doing:
def theClass = Mock(TheClass.class)
and then wire that instance into the class under unit test. What about if I want to mock a class that has been annotated as a #Singleon
Here is a little tool class you can use:
package de.scrum_master.stackoverflow
import java.lang.reflect.Field
import java.lang.reflect.Modifier
class GroovySingletonTool<T> {
private Class<T> clazz
GroovySingletonTool(Class<T> clazz) {
this.clazz = clazz
}
void setSingleton(T instance) {
// Make 'instance' field non-final
Field field = clazz.getDeclaredField("instance")
field.modifiers &= ~Modifier.FINAL
// Only works if singleton instance was unset before
field.set(clazz.instance, instance)
}
void unsetSingleton() {
setSingleton(null)
}
void reinitialiseSingleton() {
// Unset singleton instance, otherwise subsequent constructor call will fail
unsetSingleton()
setSingleton(clazz.newInstance())
}
}
Just call setSingleton(Mock(TheClass)). For more info see this answer, I do not want to repeat the whole sample code here.
Feel free to ask follow-up questions if there is anything you do not understand.
You can use global mocks
def publisher = new Publisher()
publisher << new RealSubscriber() << new RealSubscriber()
def anySubscriber = GroovyMock(RealSubscriber, global: true)
when:
publisher.publish("message")
then:
2 * anySubscriber.receive("message")

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

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.

Method aliasing in class with Groovy

I'm going to internationalize groovy API abit.
For final class (e.g. String)
String.metaClass.вСтроку = {-> this.toString() }
However, this will create additional closure. Isn't there any way to just alias method with another method?
Something like this:
String.metaClass.вСтроку = String.metaClass.&toString
You could use #Category transform like this
#Category(String) class StringInternationalization {
String вСтроку() {
this.toString()
}
int длина() {
this.length()
}
}
class ApplyMixin {
static {
String.mixin(StringInternationalization)
final helloString = "Привет мир!"
println helloString.вСтроку()
assert helloString.длина() == helloString.length()
}
}
new Main()
This will create 1 Category class for each localised class and one class to apply all mixin transformations(to register all methods.) Also should be faster, then individual closures.
More reading here: http://groovy.codehaus.org/Category+and+Mixin+transformations

Groovy: Got StackOverflowError when override invokeMethod with a closure

I'm trying to enhance my Grails project with Groovy AOP approach. However I always got StackOverflowError if I override invokeMethod with a closure. Here is my test code, I can reproduce the error with groovy 2.1.3, thanks!
class A implements GroovyInterceptable
{
void foo(){
System.out.println( "A.foo");
}
}
class B extends A
{
void foo(){
System.out.println( "B.foo");
super.foo();
}
}
def mc = B.metaClass;
mc.invokeMethod = { String name, args ->
// do "before" and/or "around" work here
try {
def value = mc.getMetaMethod(name, args).invoke(delegate, args)
// do "after" work here
return value // or another value
}
catch (e) {
// do "after-throwing" work here
}
}
B b = new B();
b.foo();
Looks like, if you have a call to super() then metaClass uses the cache to find the method and throws a StackOverflow eventually. In that case if you metaClass A instead of B, it all works fine.
def mc = A.metaClass
I can infer it this way, class implementing GroovyInterceptable directly should override invokeMethod.
#Source MetaClassImpl

Use Groovy Category implicitly in all instance methods of class

I have simple Groovy category class which adds method to String instances:
final class SampleCategory {
static String withBraces(String self) {
"($self)"
}
}
I want to use this category in my unit tests (for example). It looks like this:
class MyTest {
#Test
void shouldDoThis() {
use (SampleCategory) {
assert 'this'.withBraces() == '(this)'
}
}
#Test
void shouldDoThat() {
use (SampleCategory) {
assert 'that'.withBraces() == '(that)'
}
}
}
What I'd like to achieve, however, is ability to specify that category SampleCategory is used in scope of each and every instance method of MyTest so I don't have to specify use(SampleCategory) { ... } in every method.
Is it possible?
You can use mixin to apply the category directly to String's metaClass. Assign null to the metaClass to reset it to groovy defaults. For example:
#Before void setUp() {
String.mixin(SampleCategory)
}
#After void tearDown() {
String.metaClass = null
}
#Test
void shouldDoThat() {
assert 'that'.withBraces() == '(that)'
}
Now you have the option to use extension modules instead of categories:
http://mrhaki.blogspot.se/2013/01/groovy-goodness-adding-extra-methods.html
On the plus side Intellij will recognize the extensions. I've just noticed that it doesn't even need to be a separate module as suggested by the link, just add META-INF/services/org.codehaus.groovy.runtime.ExtensionModule to the project:
# File: src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule
moduleName = module
moduleVersion = 1.0
extensionClasses = SampleExtension
The extension class is pretty much defined like a normal category:
class SampleExtension {
static String withBraces(String self) {
"($self)"
}
}
Can be used like:
def "Sample extension"() {
expect: 'this'.withBraces() == '(this)'
}
If you are using Spock there is a #Use annotation that can be used on the specifications. The drawback with that is that Intellij will not recognize it.

Resources