Skip to content

Commit

Permalink
MockMaker API cleanup (#1868)
Browse files Browse the repository at this point in the history
Moved MockMakerId to spock.mock package.
Added release notes for mock maker feature.

Try to ease flaky InvokingMocksFromMultipleThreads spec

PollingConditions change: Fix failing test when tests are executed with non english locale.
The formatting of the elapsedTime would not match the pattern in the test.

Co-authored-by: Leonard Brünings <[email protected]>
  • Loading branch information
AndreasTu and leonard84 authored Jan 24, 2024
1 parent c6c1768 commit dd798eb
Show file tree
Hide file tree
Showing 34 changed files with 404 additions and 208 deletions.
1 change: 1 addition & 0 deletions docs/extensions.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1108,6 +1108,7 @@ The preferred mock maker will be used globally, if no mock maker is explicitly s
include::{sourcedir}/extension/MockMakerConfigurationDocSpec.groovy[tag=mock-maker-preferredMockMaker]
----

[[mock-makers-mockito]]
==== Mockito Mock Maker

The `mockito` Mock Maker provides the ability to mock final types, enums and final methods.
Expand Down
18 changes: 18 additions & 0 deletions docs/release_notes.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@ include::include.adoc[]

== 2.4 (tbd)

* Spock now supports pluggable <<extensions.adoc#mock-makers,mock makers>> loaded via ServiceLoader spockPull:1746[]
** This allows external libraries to contribute mocking logic to Spock and use the same API for the users
** You can select the used mock maker during mock creation: `Mock(mockMaker:MockMakers.byteBuddy)`
* Added <<extensions.adoc#mock-makers-mockito,mockito>> mock maker spockPull:1753[] which supports:
** Mocking of final classes and final methods
** Requires `org.mockito:mockito-core` >= 4.11 in the test class path
* Fix issue with mocks of Groovy classes, where the Groovy MOP for `@Internal` methods was not honored by the `byte-buddy` mock maker spockPull:1729[]
** This fixes multiple issues with Groovy MOP: spockIssue:1501[], spockIssue:1452[], spockIssue:1608[] and spockIssue:1145[]
* Replaced `gentyref` code with https://github.com/leangen/geantyref[geantyref] library spockPull:1743[]
** This is now a required dependency used by spock: `io.leangen.geantyref:geantyref:1.3.14`
* Better support for generic return types for mocks spockPull:1731[]
** This fixes the issues: spockIssue:520[], spockIssue:1163[]

=== Breaking Changes

* Calling `old(...)` with multiple arguments is now a compilation error. Previously the additional arguments were simply ignored.
Expand All @@ -18,6 +31,11 @@ include::include.adoc[]
* Improve `@TempDir` field injection, now it happens before field initialization, so it can be used by other field initializers.
* Fix exception when configured `baseDir` was not existing, now `@TempDir` will create the baseDir directory if it is missing.
* Fix bad error message for collection conditions, when one of the operands is `null`
* Document `@ConditionBlock` Annotation spockPull:1709[]
* Document `old`-Method spockPull:1708[]
* Clarify documentation for global Mocks spockPull:1755[]
* Spock-Compiler does not use wrapper types anymore spockPull:1765[]
* Reduce lock contention of the `byte-buddy` mock maker, when multiple mocks are created concurrently spockPull:1778[]

== 2.4-M1 (2022-11-30)

Expand Down
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ jaxb = "javax.xml.bind:jaxb-api:2.3.1"
junit4 = "junit:junit:4.13.2"
log4j = "log4j:log4j:1.2.17"
mockito4 = { module = "org.mockito:mockito-core", version.ref = "mockito4" }
mockito4inline = { module = "org.mockito:mockito-inline", version.ref = "mockito4" }
mockito5 = { module = "org.mockito:mockito-core", version.ref = "mockito5" }
objenesis = "org.objenesis:objenesis:3.3"
# This needs a classifier, but is has to be specified on the usage end https://melix.github.io/blog/2021/03/version-catalogs-faq.html#_why_can_t_i_use_excludes_or_classifiers
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.spockframework.mock;

import org.spockframework.mock.runtime.IMockMaker;
import spock.mock.IMockMakerSettings;
import org.spockframework.util.*;
import spock.mock.MockingApi;

Expand Down Expand Up @@ -95,7 +96,7 @@ public interface IMockConfiguration {
* @return the custom settings to use for the creation of the mock, or {@code null}
*/
@Nullable
IMockMaker.IMockMakerSettings getMockMaker();
IMockMakerSettings getMockMaker();

/**
* Tells whether a mock object stands in for all objects of the mocked type, or just for itself.
Expand All @@ -106,7 +107,7 @@ public interface IMockConfiguration {
boolean isGlobal();

/**
* Tells whether invocations on the mock object should be verified. If (@code false}, invocations
* Tells whether invocations on the mock object should be verified. If {@code false}, invocations
* on the mock object will not be matched against interactions that have a cardinality.
*
* @return whether invocations on the mock object should be verified
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.spockframework.util.Nullable;
import org.spockframework.util.ReflectionUtil;
import org.spockframework.util.ThreadSafe;
import spock.mock.MockMakerId;

import java.util.Collections;
import java.util.EnumSet;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.spockframework.mock.CannotCreateMockException;
import org.spockframework.util.ReflectionUtil;
import org.spockframework.util.ThreadSafe;
import spock.mock.MockMakerId;
import spock.util.environment.Jvm;

import java.util.Collections;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package org.spockframework.mock.runtime;

import groovy.lang.Closure;
import org.spockframework.mock.CannotCreateMockException;
import org.spockframework.mock.IMockObject;
import org.spockframework.mock.ISpockMockObject;
Expand All @@ -25,13 +24,13 @@
import org.spockframework.util.Immutable;
import org.spockframework.util.Nullable;
import org.spockframework.util.ThreadSafe;
import spock.mock.IMockMakerSettings;
import spock.mock.MockMakerId;
import spock.mock.MockMakers;

import java.util.List;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.regex.Pattern;

/**
* The {@code IMockMaker} provides a {@link ServiceLoader} extension point to allow the usage
Expand Down Expand Up @@ -204,47 +203,6 @@ interface IMockCreationSettings {
<T extends IMockMakerSettings> T getMockMakerSettings();
}

/**
* Settings passed to the {@link IMockMaker} and created explicitly for the requested mock maker.
* The user will create such an instance at the mocking site:
*
* <p>{@code Mock(mockMaker: yourMockSettings { yourProperty = true }, ClassToMock)}
*
* <p>The {@link IMockMaker} should define a static constant or method (see {@link MockMakers}),
* which let the user construct a custom {@link IMockMakerSettings}.
*
* <p>If you provide a method, you can define your settings in a typesafe manner for the user,
* e.g. with a {@link Closure} parameter to configure the mock.
*
* @since 2.4
*/
@Immutable
@Beta
interface IMockMakerSettings {

/**
* Creates a simple {@code IMockMakerSettings} object with only an ID.
*
* @param id the mock maker ID.
* @return the settings object
*/
static IMockMakerSettings simple(MockMakerId id) {
return new IMockMakerSettings() {
@Override
public MockMakerId getMockMakerId() {
return id;
}

@Override
public String toString() {
return id + " simple mock maker settings";
}
};
}

MockMakerId getMockMakerId();
}

/**
* {@code MockMakerCapability} describes the possible features of an {@link IMockMaker}.
*
Expand Down Expand Up @@ -310,6 +268,7 @@ IMockabilityResult notMockable() {
* @since 2.4
*/
@Beta
@ThreadSafe
interface IStaticMock {

/**
Expand All @@ -318,65 +277,22 @@ interface IStaticMock {
Class<?> getType();

/**
* Enables the static mock.
* Enables the static mock on the current thread.
* Note: The default state on creation is disabled.
*
* <p>The implementation must ensure that this method can be called multiple times for the same thread and count the activations.
* Only the first activation shall enable the static mock.
*/
void enable();

/**
* Disables the static mock.
* Disables the static mock on the current thread.
* Note: The default state on creation is disabled.
*
* <p>The implementation must ensure that this method can be called multiple times for the same thread and count the activations.
* Only the last activation shall disable the static mock.
*/
void disable();
}

/**
* Represents the ID of an {@link IMockMaker}.
*/
@Immutable
@Beta
final class MockMakerId implements Comparable<MockMakerId> {
private static final Pattern VALID_ID = Pattern.compile("^[a-z][a-z0-9-]*+(?<!-)$");
private final String id;

public MockMakerId(String id) {
if (id == null) {
throw new IllegalArgumentException("The ID is null.");
}
if (id.isEmpty()) {
throw new IllegalArgumentException("The ID is empty.");
}
if (!VALID_ID.matcher(id).matches()) {
throw new IllegalArgumentException("The ID '" + id + "' is invalid. Valid id must comply to the pattern: " + VALID_ID);
}
this.id = id;
}

@Override
public int compareTo(MockMakerId o) {
return this.id.compareTo(o.id);
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MockMakerId that = (MockMakerId) o;
return Objects.equals(id, that.id);
}

@Override
public int hashCode() {
return Objects.hash(id);
}

@Override
public String toString() {
return id;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.spockframework.mock.CannotCreateMockException;
import org.spockframework.mock.ISpockMockObject;
import org.spockframework.util.ThreadSafe;
import spock.mock.MockMakerId;

import java.lang.reflect.Proxy;
import java.util.ArrayList;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import org.spockframework.mock.*;
import org.spockframework.util.*;
import spock.mock.IMockMakerSettings;

import java.lang.reflect.Type;
import java.util.*;
Expand All @@ -36,7 +37,7 @@ public class MockConfiguration implements IMockConfiguration {
private final boolean global;
private final boolean verified;
private final boolean useObjenesis;
private final IMockMaker.IMockMakerSettings mockMakerSettings;
private final IMockMakerSettings mockMakerSettings;

public MockConfiguration(@Nullable String name, Type type, MockNature nature,
MockImplementation implementation, Map<String, Object> options) {
Expand All @@ -57,7 +58,7 @@ public MockConfiguration(@Nullable String name, Type type, @Nullable Object inst
this.global = getOption(options, "global", Boolean.class, false);
this.verified = getOption(options, "verified", Boolean.class, this.nature.isVerified());
this.useObjenesis = getOption(options, "useObjenesis", Boolean.class, this.nature.isUseObjenesis());
this.mockMakerSettings = getOption(options, "mockMaker", IMockMaker.IMockMakerSettings.class, null);
this.mockMakerSettings = getOption(options, "mockMaker", IMockMakerSettings.class, null);
}

@Override
Expand Down Expand Up @@ -123,7 +124,7 @@ public boolean isUseObjenesis() {
}

@Override
public IMockMaker.IMockMakerSettings getMockMaker() {
public IMockMakerSettings getMockMaker() {
return mockMakerSettings;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
import org.spockframework.mock.MockNature;
import org.spockframework.util.Beta;
import org.spockframework.util.Nullable;
import spock.mock.IMockMakerSettings;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static java.util.Objects.requireNonNull;
Expand All @@ -30,7 +32,7 @@
@Beta
public class MockCreationSettings implements IMockMaker.IMockCreationSettings {

private final IMockMaker.IMockMakerSettings mockMakerSettings;
private final IMockMakerSettings mockMakerSettings;
private final Class<?> mockType;
private final ClassLoader classLoader;
private final boolean useObjenesis;
Expand All @@ -49,7 +51,7 @@ public static MockCreationSettings settings(Class<?> mockType, List<Class<?>> ad
return new MockCreationSettings(null, mockType, MockNature.MOCK, additionalInterfaces, null, interceptor, classLoader, useObjenesis, false);
}

MockCreationSettings(@Nullable IMockMaker.IMockMakerSettings mockMakerSettings,
private MockCreationSettings(@Nullable IMockMakerSettings mockMakerSettings,
Class<?> mockType,
MockNature mockNature,
List<Class<?>> additionalInterfaces,
Expand Down Expand Up @@ -110,7 +112,7 @@ public boolean isStaticMock() {
}

@Override
public <T extends IMockMaker.IMockMakerSettings> T getMockMakerSettings() {
public <T extends IMockMakerSettings> T getMockMakerSettings() {
return uncheckedCast(mockMakerSettings);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.spockframework.util.Beta;
import org.spockframework.util.Nullable;
import spock.config.ConfigurationObject;
import spock.mock.IMockMakerSettings;
import spock.mock.MockMakers;

/**
Expand All @@ -43,5 +44,5 @@ public class MockMakerConfiguration {
* You can find the built-in mock makers in {@link MockMakers}.
*/
@Nullable
public IMockMaker.IMockMakerSettings preferredMockMaker = null;
public IMockMakerSettings preferredMockMaker = null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import org.spockframework.util.InternalSpockError;
import org.spockframework.util.Nullable;
import org.spockframework.util.ThreadSafe;
import spock.mock.IMockMakerSettings;
import spock.mock.MockMakerId;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
Expand All @@ -38,8 +40,8 @@
import java.util.function.BiFunction;
import java.util.stream.Collectors;

import static org.spockframework.mock.runtime.IMockMaker.MockMakerId;
import static org.spockframework.mock.runtime.IMockMaker.IMockMakerSettings;
import static java.util.Objects.requireNonNull;

import static org.spockframework.mock.runtime.IMockMaker.IMockCreationSettings;
import static org.spockframework.mock.runtime.IMockMaker.IMockabilityResult;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.spockframework.mock.runtime.IMockMaker;
import org.spockframework.util.ReflectionUtil;
import org.spockframework.util.ThreadSafe;
import spock.mock.MockMakerId;

import java.lang.reflect.Modifier;
import java.util.Collections;
Expand Down
Loading

0 comments on commit dd798eb

Please sign in to comment.