Skip to content

Commit

Permalink
Static mocking documentation updates (#1907)
Browse files Browse the repository at this point in the history
Moved static mocking into own section.
Added sample to do stubbing of all static methods.
  • Loading branch information
AndreasTu authored Mar 3, 2024
1 parent 77deb9a commit 906d016
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 67 deletions.
2 changes: 1 addition & 1 deletion docs/extensions.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1220,7 +1220,7 @@ include::{sourcedir}/interaction/MockMakerDocSpec.groovy[tag=mock-serializable]
The `mockito` mock maker uses `org.mockito.MockMakers.INLINE` under the hood,
please see the Mockito manual "Mocking final types, enums and final methods" for all pros and cons,
when using `org.mockito.MockMakers.INLINE`.
It also supports mocking of static methods of classes and interfaces with `Mock/Stub/SpyStatic`.
It also supports mocking of static methods of classes and interfaces with `SpyStatic`.
See <<interaction_based_testing.adoc#static-mocks,static mocks>> section for more details.

==== Custom Mock Maker
Expand Down
142 changes: 77 additions & 65 deletions docs/interaction_based_testing.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,79 @@ then:
1 * persister.persist("msg")
----

[[MockingStaticMethods]]
== Mocking Static Methods

Spock supports two ways of mocking static methods:

* `SpyStatic()` static mocks: Works with Java and Groovy, but requires a mock maker supporting this, e.g. <<extensions.adoc#mock-makers-mockito,mockito>> Mock Maker.
* <<#global-groovy-mock-static-methods,Global Groovy mocks>>: Work only for Groovy code not for Java.

NOTE: Think twice before using this feature.
It might be better to change the design of the code under specification.

[[static-mocks]]
=== Static Mocks

You can create static mocks with `SpyStatic()`.

`SpyStatic(<Type>)` mocks the static methods of the given type; by default, delegates all calls to the real static methods.
Supports both stubbing and mocking.

We are using the class `StaticClass` in the examples:

.Example static class used in the test examples
[source,groovy,indent=0]
----
include::{sourcedir}/interaction/StaticSpyDocSpec.groovy[tag=mock-static-class]
----

We want to mock the method `staticMethod()`, so we can create a static mock for the `StaticClass` type:

.Mock static method of a class with SpyStatic()
[source,groovy,indent=0]
----
include::{sourcedir}/interaction/StaticSpyDocSpec.groovy[tag=mock-static1]
----

You can also specify the answers after construction by defining the interactions as for a normal mock:

.Mock static method of a class with interactions
[source,groovy,indent=0]
----
include::{sourcedir}/interaction/StaticSpyDocSpec.groovy[tag=mock-static-interactions]
----

It is also possible to stub all static methods of a class like a `Stub` would do:

.Stub all static methods of a class with default interaction
[source,groovy,indent=0]
----
include::{sourcedir}/interaction/StaticSpyDocSpec.groovy[tag=stub-all-static-methods]
----

NOTE: The static mocks require a mock maker supporting static methods, e.g. <<extensions.adoc#mock-makers-mockito,mockito>>.
See <<extensions.adoc#mock-makers,mock makers>> table for mock makers supporting it.

=== Static Mocks and Threading

The static mocks are thread-local, so they do not interfere with concurrent test execution.
But this also means that a static mock will not be active, if your code under test will use other threads.

A static mock is activated on the thread, which created the mock, up until the feature execution ends.
You can activate all static mocks on a different thread by hand:

.Use static mocks in a different Thread
[source,groovy,indent=0]
----
include::{sourcedir}/interaction/StaticSpyDocSpec.groovy[tag=mock-static-different-thread]
----

The `spock.mock.MockingApi` provides methods to activate a static mock on other threads:

* `runWithThreadAwareMocks(Runnable)`
* `withActiveThreadAwareMocks(Callable)`

[[GroovyMocks]]
== Groovy Mocks

Expand Down Expand Up @@ -1027,72 +1100,11 @@ new RealSubscriber("Fred") >> new RealSubscriber("Barney")
Now, whenever some code tries to construct a subscriber named Fred, we'll construct
a subscriber named Barney instead.

[MockingStaticMethods]
=== Mocking Static Methods

Spock supports two ways of mocking static methods:

* `SpyStatic()` static mocks: Works with Java and Groovy, but requires a mock maker supporting this, e.g. <<extensions.adoc#mock-makers-mockito,mockito>> Mock Maker.
* Global Groovy mocks: Work only for Groovy code not for Java. If parallel execution is enabled, then the test must be annotated with `@ResourceLock(Resources.META_CLASS_REGISTRY)` or `@Isolated`.

NOTE: Think twice before using this feature.
It might be better to change the design of the code under specification.

[[static-mocks]]
==== Static Mocks

You can create static mocks with `SpyStatic()`.
The semantics are the same as for the non-static variants:

* `SpyStatic()`: Mocks static methods of the given type; by default, delegates all calls to the real static methods.
Supports both stubbing and mocking.

We are using the class `StaticClass` in the examples:

.Example static class used in the test examples
[source,groovy,indent=0]
----
include::{sourcedir}/interaction/StaticSpyDocSpec.groovy[tag=mock-static-class]
----

We want to mock the method `staticMethod()`, so we can create a static mock for the `StaticClass` type:

.Mock static method of a class with SpyStatic()
[source,groovy,indent=0]
----
include::{sourcedir}/interaction/StaticSpyDocSpec.groovy[tag=mock-static1]
----

You can also specify the answers after construction by defining the interactions as for a normal mock:

.Mock static method of a class with interactions
[source,groovy,indent=0]
----
include::{sourcedir}/interaction/StaticSpyDocSpec.groovy[tag=mock-static-interactions]
----

NOTE: The static mocks require a mock maker supporting static methods, e.g. <<extensions.adoc#mock-makers-mockito,mockito>>.
See <<extensions.adoc#mock-makers,mock makers>> table for mock makers supporting it.

===== Static Mocks and Threading

The static mocks are thread-local, so they do not interfere with concurrent test execution.
But this also means that a static mock will not be active, if your code under test will use other threads.

A static mock is activated on the thread, which created the mock, up until the feature execution ends.
You can activate all static mocks on a different thread by hand:

.Use static mocks in a different Thread
[source,groovy,indent=0]
----
include::{sourcedir}/interaction/StaticSpyDocSpec.groovy[tag=mock-static-different-thread]
----

The interface `spock.mock.IStaticMock` provides API to activate a static mock on other threads.

==== Global Groovy Mocks for Static Methods
[[global-groovy-mock-static-methods]]
=== Global Groovy Mocks for Static Methods

Global mocks support mocking and stubbing of static methods:
Global mocks support mocking and stubbing of static methods in Groovy code.
If you want to mock static methods for Java code please use <<#static-mocks,Static Mocks>>.

[source,groovy]
----
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

package org.spockframework.docs.interaction


import org.spockframework.mock.ZeroOrNullResponse
import spock.lang.Specification

import java.util.concurrent.Executors
Expand Down Expand Up @@ -51,6 +51,28 @@ class StaticSpyDocSpec extends Specification {
// end::mock-static-interactions[]
}

def "SpyStatic mock all static methods"() {
given:
SpyStatic(StaticClass)
StaticClass._ >> { ZeroOrNullResponse.INSTANCE.respond(delegate) }

expect:
StaticClass.staticMethod() == null
StaticClass.otherStaticMethod() == null
}

def "SpyStatic stub all static methods"() {
// tag::stub-all-static-methods[]
given:
SpyStatic(StaticClass)
StaticClass._ >> _

expect:
StaticClass.staticMethod() == ""
StaticClass.otherStaticMethod() == ""
// end::stub-all-static-methods[]
}

def "SpyStatic in a different thread"() {
// tag::mock-static-different-thread[]
given:
Expand Down Expand Up @@ -87,6 +109,10 @@ class StaticSpyDocSpec extends Specification {
static String staticMethod() {
return "RealValue"
}

static String otherStaticMethod() {
return "OtherValue"
}
}
// end::mock-static-class[]
}

0 comments on commit 906d016

Please sign in to comment.