= Extensions

Spock comes with a powerful extension mechanism, which allows to hook into a spec's lifecycle to enrich or alter its
behavior. In this chapter, we will first learn about Spock's built-in extensions, and then dive into writing custom
extensions.

== Built-In Extensions

Most of Spock's built-in extensions are _annotation-driven_. In other words, they are triggered by annotating a
spec class or method with a certain annotation. You can tell such an annotation by its +@ExtensionAnnotation+
meta-annotation.

=== Ignore

To temporarily prevent a feature method from getting executed, annotate it with +spock.lang.Ignore+:

[source,groovy]
----
@Ignore
def "my feature"() { ... }
----

For documentation purposes, a reason can be provided:

[source,groovy]
----
@Ignore(reason = "TODO")
def "my feature"() { ... }
----

To ignore a whole specification, annotate its class:

[source,groovy]
----
@Ignore
class MySpec extends Specification { ... }
----

In most execution environments, ignored feature methods and specs will be reported as "skipped".

=== IgnoreRest

To ignore all but a (typically) small subset of methods, annotate the latter with +spock.lang.IgnoreRest+:

[source,groovy]
----
def "I'll be ignored"() { ... }

@IgnoreRest
def "I'll run"() { ... }

def "I'll also be ignored"() { ... }
----

+@IgnoreRest+ is especially handy in execution environments that don't provide an (easy) way to run a subset of methods.

=== IgnoreIf

To ignore a feature method under certain conditions, annotate it with +spock.lang.IgnoreIf+,
followed by a predicate:

[source,groovy]
----
@IgnoreIf({ System.getProperty("os.name").contains("windows") })
def "I'll run everywhere but on Windows"() { ... }
----

To make predicates easier to read and write, the following properties are available inside the closure:

* `sys` A map of all system properties
* `env` A map of all environment variables
* `os` Information about the operating system (see `spock.util.environment.OperatingSystem`)
* `jvm` Information about the JVM (see `spock.util.environment.Jvm`)

Using the `os` property, the previous example can be rewritten as:

[source,groovy]
----
@IgnoreIf({ os.windows })
def "I'll run everywhere but on Windows"() { ... }
----

=== Requires

To execute a feature method under certain conditions, annotate it with `spock.lang.Requires`,
followed by a predicate:

[source,groovy]
----
@Requires({ os.windows })
def "I'll only run on Windows"() { ... }
----

`Requires` works exactly like `IgnoreIf`, except that the predicate is inverted. In general, it is preferable
to state the conditions under which a method gets executed, rather than the conditions under which it gets ignored.


=== Stepwise

To execute features in the order that they are declared, annotate a spec class with `spock.lang.Stepwise`:

[source,groovy]
----
@Stepwise
class RunInOrderSpec extends Specification {
  def "I run first"()  { ... }
  def "I run second"() { ... }
}
----

`Stepwise` only affects the class carrying the annotation; not sub or super classes.  Features after the first
failure are skipped.


=== Timeout

To fail a feature method, fixture, or class that exceeds a given execution duration, use `spock.lang.Timeout`,
followed by a duration, and optionally a time unit. The default time unit is seconds.

When applied to a feature method, the timeout is per execution of one iteration, excluding time spent in fixture methods:

[source,groovy]
----
@Timeout(5)
def "I fail if I run for more than five seconds"() { ... }

@Timeout(value = 100, unit = TimeUnit.MILLISECONDS)
def "I better be quick" { ... }
----

Applying `Timeout` to a spec class has the same effect as applying it to each feature that is not already annotated
with `Timeout`, excluding time spent in fixtures:

[source,groovy]
----
@Timeout(10)
class TimedSpec extends Specification {
  def "I fail after ten seconds"() { ... }
  def "Me too"() { ... }

  @Timeout(value = 250, unit = MILLISECONDS)
  def "I fail much faster"() { ... }
}
----

When applied to a fixture method, the timeout is per execution of the fixture method.

When a timeout is reported to the user, the stack trace shown reflects the execution stack of the test framework when
the timeout was exceeded.

=== Use

To activate one or more Groovy categories within the scope of a feature method or spec, use `spock.util.mop.Use`:

[source,groovy]
----
class ListExtensions {
  static avg(List list) { list.sum() / list.size() }
}

class MySpec extends Specification {
  @Use(listExtensions)
  def "can use avg() method"() {
    expect:
    [1, 2, 3].avg() == 2
  }
}
----

This can be useful for stubbing of dynamic methods, which are usually provided by the runtime environment (e.g. Grails).
It has no effect when applied to a helper method. However, when applied to a spec class, it will also affect its helper
methods.


=== ConfineMetaClassChanges

To confine meta class changes to the scope of a feature method or spec class, use `spock.util.mop.ConfineMetaClassChanges`:

[source,groovy]
----
@Stepwise
class FooSpec extends Specification {
  @ConfineMetaClassChanges
  def "I run first"() {
    when:
    String.metaClass.someMethod = { delegate }

    then:
    String.metaClass.hasMetaMethod('someMethod')
  }

  def "I run second"() {
    when:
    "Foo".someMethod()

    then:
    thrown(MissingMethodException)
  }
}
----

When applied to a spec class, the meta classes are restored to the state that they were in before `setupSpec` was executed,
after `cleanupSpec` is executed.

When applied to a feature method, the meta classes are restored to as they were after `setup` was executed,
before `cleanup` is executed.

=== AutoCleanup

Automatically clean up a field or property at the end of its lifetime by using `spock.lang.AutoCleanup`.

By default, an object is cleaned up by invoking its parameterless `close()` method. If some other
method should be called instead, override the annotation's `value` attribute:

[source,groovy]
----
// invoke foo.dispose()
@AutoCleanup("dispose")
def foo
----

If multiple fields or properties are annotated with `AutoCleanup`, their objects are cleaned up sequentially, in reverse
field/property declaration order, starting from the most derived class class and walking up the inheritance chain.

If a cleanup operation fails with an exception, the exception is reported by default, and cleanup proceeds with the next
annotated object. To prevent cleanup exceptions from being reported, override the annotation's `quiet` attribute:

[source,groovy]
----
@AutoCleanup(quiet = true)
def ignoreMyExceptions
----

=== Title and Narrative

To attach a natural-language name to a spec, use `spock.lang.Title`:

[source,groovy]
----
@Title("This is easy to read")
class ThisIsHarderToReadSpec extends Specification {
  ...
}
----

Similarly, to attach a natural-language description to a spec, use `spock.lang.Narrative`:

[source,groovy]
----
@Narrative("""
As a user
I want foo
So that bar
""")
class GiveTheUserFooSpec() { ... }
----

=== Issue

To indicate that a feature or spec relates to one or more issues in an external tracking system, use `spock.lang.Issue`:

[source,groovy]
----
@Issue("http://my.issues.org/FOO-1")
class MySpec {
  @Issue("http://my.issues.org/FOO-2")
  def "Foo should do bar"() { ... }

  @Issue(["http://my.issues.org/FOO-3", "http://my.issues.org/FOO-4"])
  def "I have two related issues"() { ... }
}
----

=== Subject

To indicate one or more subjects of a spec, use `spock.lang.Subject`:

[source,groovy]
----
@Subject([Foo, Bar]) { ... }
----

Additionally, `Subject` can be applied to fields and local variables:

[source,groovy]
----
@Subject
Foo myFoo
----

`Subject` currently has only informational purposes.

TODO More to follow.

== Writing Custom Extensions

TODO

