Class JunitExtensionSupport

  • All Implemented Interfaces:
    org.spockframework.runtime.extension.IGlobalExtension

    public class JunitExtensionSupport
    extends java.lang.Object
    implements org.spockframework.runtime.extension.IGlobalExtension
    Global extension applied to all specs. Searches for declared junit extensions and calls them simulating junit extensions lifecycle. Supported all the same declaration methods as with junit:
    • @ExtendsWith declaration on class, method, field or parameter
    • Custom annotations on class, method, field or parameter
    • @RegisterExtension on static or non-static fields
    • Parameters injection into fixture and test methods
    • ExecutionConditions on class or method (so, for example, Disabled will force skipping tests.

    Junit extensions are executed before spock extensions. For example, BeforeAllCallback extension would be executed before spock extensions using interceptSetupSpecMethod (because extension is global it would be executed before all other spock extensions and that's why junit extensions will work in priority).

    Supported almost all junit extension types, except ExtensionUtils.UNSUPPORTED_EXTENSIONS (exception handling, test watching, invocation interceptor and of course test instance factory). If not supported extension type would be detected in extension, warning will be logged.

    Auto-detected (service loader declared extensions), default and synthetic junit extensions are not supported: auto-detection would be not obvious (such extensions could always be registered manually), and the other two are too jupiter specific.

    Implementation copies and re-use many jupiter-engine mechanisms (junit-jupiter-engine artifact) and so works (mostly) exactly the same as in jupiter (5.8). There are comments all over the code for jupiter-engine reference implementations.

    Overall workflow:

    • for each spec extension registry (ExtensionRegistry is always created, containing all found extensions.
    • ClassContext created to represent spec-level context (it will be used as parameter for before/after all and instance post processor extensions)
    • Before feature method execution, extended ExtensionRegistry is created
    • MethodContext created for feature level (it will be used as parameter for all other extensions)
    • Note that fresh method context is created for each method execution: in case of data-driven methods, each iteration will have its own context (required because method extensions must be renewed for each execution)
    • Pre destroy extension is called after cleanup (after cleanup methods and cleanup of spock extensions)

    Spock's shared state is not used: don't mark RegisterExtension extensions with @Shared - they will always be null as spock manage them on different instance. Use static fields to declare spec-wide extensions (same as in jupiter).

    Special API added to allow SPOCK extensions accessing junit shared state (used by all extensions to store values): getStore(SpecInfo, Namespace) and getStore(IMethodInvocation, Namespace). This might be used by spock extension authors to access junit state values or to simply using junit state as there is no alternative feature in spock itself.

    Since:
    25.11.2021
    See Also:
    for lifecycle details
    • Method Summary

      All Methods Static Methods Instance Methods Concrete Methods 
      Modifier and Type Method Description
      static org.junit.jupiter.api.extension.ExtensionContext.Store getStore​(org.spockframework.runtime.extension.IMethodInvocation invocation, org.junit.jupiter.api.extension.ExtensionContext.Namespace namespace)
      In contrast to getStore(SpecInfo, Namespace) provide either class-level or method-level context (depends on test instance presence).
      static org.junit.jupiter.api.extension.ExtensionContext.Store getStore​(org.spockframework.runtime.model.SpecInfo spec, org.junit.jupiter.api.extension.ExtensionContext.Namespace namespace)
      Storage used by junit extensions to keep local state (see docs).
      void start()  
      void stop()  
      void visitSpec​(org.spockframework.runtime.model.SpecInfo spec)  
      • Methods inherited from class java.lang.Object

        clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    • Constructor Detail

      • JunitExtensionSupport

        public JunitExtensionSupport()
    • Method Detail

      • start

        public void start()
        Specified by:
        start in interface org.spockframework.runtime.extension.IGlobalExtension
      • stop

        public void stop()
        Specified by:
        stop in interface org.spockframework.runtime.extension.IGlobalExtension
      • visitSpec

        public void visitSpec​(org.spockframework.runtime.model.SpecInfo spec)
        Specified by:
        visitSpec in interface org.spockframework.runtime.extension.IGlobalExtension
      • getStore

        public static org.junit.jupiter.api.extension.ExtensionContext.Store getStore​(org.spockframework.runtime.model.SpecInfo spec,
                                                                                      org.junit.jupiter.api.extension.ExtensionContext.Namespace namespace)
        Storage used by junit extensions to keep local state (see docs). Spock does not have anything like this. This method allows spock extension authors to access storage used by junit extensions and so access contained values. May be also used for storing values by spock extensions.

        Storage is hierarchical: there are spec level storage (used by ConditionEvaluator, BeforeAllCallback, AfterAllCallback and TestInstancePreDestroyCallback when no test instance is available). For each test instance created for feature (test method or test method data-iteration) new storage created (see getStore(IMethodInvocation, Namespace) for accessing). Child storage level could see all parent values, but not modify them.

        In short: this method provides class-wide (spec-wide) storage, which values are visible in all test methods. This is the same call as ExtensionContext.getStore(Namespace). Method might be called at any time because extension is global and so will work before any other custom spock extension.

        Parameters:
        spec - specification instance
        namespace - target namespace
        Returns:
        namespaced storage instance
        Throws:
        java.lang.NullPointerException - if junit extension interceptor could not be found
      • getStore

        public static org.junit.jupiter.api.extension.ExtensionContext.Store getStore​(org.spockframework.runtime.extension.IMethodInvocation invocation,
                                                                                      org.junit.jupiter.api.extension.ExtensionContext.Namespace namespace)
        In contrast to getStore(SpecInfo, Namespace) provide either class-level or method-level context (depends on test instance presence). In most cases simply use this method to get storage from the most actual context level (but if you need only root level use spec-based method: for example, might be useful if you need to modify root storage values).

        Feature-wide storage context created on just after spock initialization event (not shared initialization!) and destroyed after cleanup event. New instance created for EACH test method execution (in case of data-driven tests for each iteration!).

        Method level context is used for junit extensions: BeforeEachCallback, BeforeTestExecutionCallback, AfterTestExecutionCallback, ParameterResolver, AfterEachCallback and TestInstancePreDestroyCallback.

        This is the same call as ExtensionContext.getStore(Namespace). Method context will see all values from spec-level, but will not be able to modify them (if you try to set value it would modify method level only; same for remove).

        Parameters:
        invocation - spock extension parameter
        namespace - target namespace
        Returns:
        namespaced storage instance (method or class level)
        Throws:
        java.lang.NullPointerException - if junit extension interception not found, test instance is not yet available or method context not found when should be