Skip to content

Commit

Permalink
Mocking in given is ignored when mocked in setup
Browse files Browse the repository at this point in the history
The Interactions now allow to override other
interactions, if these are the same and do not
specify a cardinality.

This fixes spockframework#962
  • Loading branch information
AndreasTu committed Aug 14, 2023
1 parent e3ec836 commit a728e3c
Show file tree
Hide file tree
Showing 27 changed files with 852 additions and 9 deletions.
32 changes: 32 additions & 0 deletions docs/interaction_based_testing.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,38 @@ _ * subscriber.receive("hello") // any number of calls, including zero
// (rarely needed; see 'Strict Mocking')
----

==== Interactions with Cardinality

Interactions with cardinality are additive, this means if you specify two interactions with cardinality (with same target, method and argument)
they are processed after each other:

.Interactions with Cardinality are additive
[source,groovy,indent=0]
----
include::{sourcedir}/interaction/InteractionDocSpec.groovy[tag=two-interactions-cardinality]
----

==== Interactions without Cardinality

Interactions without cardinality are **not** additive.
If you specify multiple interactions without cardinality (with same target, method and argument)
only the last interaction is respected.

That allows you to specify default interactions in the `setup` method, but override it in
some of the `feature` methods.


.Interactions without Cardinality are **not** additive
[source,groovy,indent=0]
----
include::{sourcedir}/interaction/InteractionDocSpec.groovy[tag=two-interactions-without-cardinality]
----

NOTE: An interaction without cardinality will **not** override an interaction with cardinality and vice versa.

CAUTION: Interactions in `then` blocks have always precedence over any other interactions regardless of their cardinality.
So an interaction in a `then` block will apply regardless what was specified in `setup`, `given` or the `when`.

=== Target Constraint

The target constraint of an interaction describes which mock object is expected to receive the method call:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,26 @@

import org.spockframework.util.UnreachableCodeError;

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

abstract class DefaultInteraction implements IMockInteraction {

@Override
public boolean isCardinalitySpecified() {
return false;
}

@Override
public List<IInvocationConstraint> getConstraints() {
return Collections.emptyList();
}

@Override
public boolean isThisInteractionOverridableBy(IMockInteraction toCheck) {
return false;
}

@Override
public int getLine() {
return -1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
* @author Peter Niederwieser
*/
public interface IArgumentConstraint {
/**
* @param other the other element
* @return {@code true}, if both elements represent the same {@link IArgumentConstraint} from the declaration point of view
*/
boolean isDeclarationEqualTo(IArgumentConstraint other);
boolean isSatisfiedBy(Object arg);
String describeMismatch(Object arg);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
* @author Peter Niederwieser
*/
public interface IInvocationConstraint {
/**
* @param other the other element
* @return {@code true}, if both elements represent the same {@link IInvocationConstraint} from the declaration point of view
*/
boolean isDeclarationEqualTo(IInvocationConstraint other);
boolean isSatisfiedBy(IMockInvocation invocation);
String describeMismatch(IMockInvocation invocation);
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,27 @@ public interface IMockInteraction {

String getText();

/**
* @return {@code true}, if the user had specified cardinality
*/
boolean isCardinalitySpecified();

List<IInvocationConstraint> getConstraints();

/**
* Returns {@code true}, if this interaction, can be overridden by the {@link IMockInteraction} {@code toCheck}.
*
* <p>This is possible if:
* <ul>
* <li>Both have no specified cardinality</li>
* <li>Both have the same {@link IInvocationConstraint}s, see {@link IInvocationConstraint#isDeclarationEqualTo(IInvocationConstraint)}</li>
* </ul>
*
* @param toCheck the element, which wants to override this.
* @return {@code true}, if this can be overridden.
*/
boolean isThisInteractionOverridableBy(IMockInteraction toCheck);

boolean matches(IMockInvocation invocation);

@Nullable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,28 @@

import groovy.lang.Closure;

import java.util.Objects;

/**
*
* @author Peter Niederwieser
*/
public class CodeArgumentConstraint implements IArgumentConstraint {
private final Closure code;
private final Closure<?> code;

public CodeArgumentConstraint(Closure code) {
public CodeArgumentConstraint(Closure<?> code) {
this.code = code;
}

@Override
public boolean isDeclarationEqualTo(IArgumentConstraint other) {
if (other instanceof CodeArgumentConstraint) {
CodeArgumentConstraint o = (CodeArgumentConstraint) other;
return Objects.equals(this.code, o.code);
}
return false;
}

@Override
public boolean isSatisfiedBy(Object argument) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ public EqualArgumentConstraint(Object expected) {
this.expected = expected;
}

@Override
public boolean isDeclarationEqualTo(IArgumentConstraint other) {
if (other instanceof EqualArgumentConstraint) {
EqualArgumentConstraint o = (EqualArgumentConstraint) other;
return this.expected == o.expected;
}
return false;
}

@Override
public boolean isSatisfiedBy(Object arg) {
if (HamcrestFacade.isMatcher(expected)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import org.spockframework.runtime.Condition;
import org.spockframework.util.CollectionUtil;

import java.util.Objects;

/**
*
* @author Peter Niederwieser
Expand All @@ -31,6 +33,15 @@ public EqualMethodNameConstraint(String methodName) {
this.methodName = methodName;
}

@Override
public boolean isDeclarationEqualTo(IInvocationConstraint other) {
if (other instanceof EqualMethodNameConstraint) {
EqualMethodNameConstraint o = (EqualMethodNameConstraint) other;
return Objects.equals(this.methodName, o.methodName);
}
return false;
}

@Override
public boolean isSatisfiedBy(IMockInvocation invocation) {
return invocation.getMethod().getName().equals(methodName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@

package org.spockframework.mock.constraint;

import org.spockframework.mock.IInvocationConstraint;
import org.spockframework.mock.IMockInvocation;
import org.spockframework.runtime.Condition;
import org.spockframework.util.CollectionUtil;

import java.util.Objects;

/**
*
* @author Peter Niederwieser
Expand All @@ -29,6 +32,15 @@ public EqualPropertyNameConstraint(String propertyName) {
this.propertyName = propertyName;
}

@Override
public boolean isDeclarationEqualTo(IInvocationConstraint other) {
if (other instanceof EqualPropertyNameConstraint) {
EqualPropertyNameConstraint o = (EqualPropertyNameConstraint) other;
return Objects.equals(this.propertyName, o.propertyName);
}
return false;
}

@Override
public boolean isSatisfiedBy(IMockInvocation invocation) {
return propertyName.equals(getPropertyName(invocation));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ public NamedArgumentListConstraint(List<Object> argNames, List<IArgumentConstrai
this.argConstraints = argConstraints;
}

@Override
public boolean isDeclarationEqualTo(IInvocationConstraint other) {
if (other instanceof NamedArgumentListConstraint) {
NamedArgumentListConstraint o = (NamedArgumentListConstraint) other;
return Objects.equals(this.argNames, o.argNames) &&
CollectionUtil.areListsEqual(this.argConstraints, o.argConstraints, IArgumentConstraint::isDeclarationEqualTo) &&
this.isMixed == o.isMixed;
}
return false;
}

@Override
@SuppressWarnings("unchecked")
public boolean isSatisfiedBy(IMockInvocation invocation) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ public NegatingArgumentConstraint(IArgumentConstraint constraint) {
this.constraint = constraint;
}

@Override
public boolean isDeclarationEqualTo(IArgumentConstraint other) {
if (other instanceof NegatingArgumentConstraint) {
NegatingArgumentConstraint o = (NegatingArgumentConstraint) other;
return this.constraint.isDeclarationEqualTo(o.constraint);
}
return false;
}

@Override
public boolean isSatisfiedBy(Object arg) {
return !constraint.isSatisfiedBy(arg);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@ public PositionalArgumentListConstraint(List<IArgumentConstraint> argConstraints
}
}

@Override
public boolean isDeclarationEqualTo(IInvocationConstraint other) {
if (other instanceof PositionalArgumentListConstraint) {
PositionalArgumentListConstraint o = (PositionalArgumentListConstraint) other;
return CollectionUtil.areListsEqual(this.argConstraints, o.argConstraints, IArgumentConstraint::isDeclarationEqualTo);
}
return false;
}

@Override
public boolean isSatisfiedBy(IMockInvocation invocation) {
List<Object> args = invocation.getArguments();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.spockframework.runtime.Condition;
import org.spockframework.util.CollectionUtil;

import java.util.Objects;
import java.util.regex.Pattern;

/**
Expand All @@ -33,6 +34,15 @@ public RegexMethodNameConstraint(String regex) {
pattern = Pattern.compile(regex);
}

@Override
public boolean isDeclarationEqualTo(IInvocationConstraint other) {
if (other instanceof RegexMethodNameConstraint) {
RegexMethodNameConstraint o = (RegexMethodNameConstraint) other;
return Objects.equals(this.pattern.toString(), o.pattern.toString());
}
return false;
}

@Override
public boolean isSatisfiedBy(IMockInvocation invocation) {
return pattern.matcher(invocation.getMethod().getName()).matches();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.spockframework.runtime.*;
import org.spockframework.util.CollectionUtil;

import java.util.Objects;
import java.util.regex.Pattern;

/**
Expand All @@ -31,6 +32,15 @@ public RegexPropertyNameConstraint(String regex) {
pattern = Pattern.compile(regex);
}

@Override
public boolean isDeclarationEqualTo(IInvocationConstraint other) {
if (other instanceof RegexPropertyNameConstraint) {
RegexPropertyNameConstraint o = (RegexPropertyNameConstraint) other;
return Objects.equals(this.pattern.toString(), o.pattern.toString());
}
return false;
}

@Override
public boolean isSatisfiedBy(IMockInvocation invocation) {
String propertyName = getPropertyName(invocation);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ public class SpreadWildcardArgumentConstraint implements IArgumentConstraint {

private SpreadWildcardArgumentConstraint() {}

@Override
public boolean isDeclarationEqualTo(IArgumentConstraint other) {
return this == other;
}

@Override
public boolean isSatisfiedBy(Object arg) {
throw new InvalidSpecException("*_ may only appear at the end of an argument list");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ public TargetConstraint(Object target) {
this.target = target;
}

@Override
public boolean isDeclarationEqualTo(IInvocationConstraint other) {
if (other instanceof TargetConstraint) {
TargetConstraint o = (TargetConstraint) other;
return this.target == o.target;
}
return false;
}

@Override
public boolean isSatisfiedBy(IMockInvocation invocation) {
return invocation.getMockObject().matches(target, interaction);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import org.spockframework.runtime.Condition;
import org.spockframework.util.CollectionUtil;

import java.util.Objects;

/**
*
* @author Peter Niederwieser
Expand All @@ -33,6 +35,16 @@ public TypeArgumentConstraint(Class<?> type, IArgumentConstraint constraint) {
this.constraint = constraint;
}

@Override
public boolean isDeclarationEqualTo(IArgumentConstraint other) {
if (other instanceof TypeArgumentConstraint) {
TypeArgumentConstraint o = (TypeArgumentConstraint) other;
return Objects.equals(this.type, o.type) &&
this.constraint.isDeclarationEqualTo(o.constraint);
}
return false;
}

@Override
public boolean isSatisfiedBy(Object argument) {
return type.isInstance(argument) && constraint.isSatisfiedBy(argument);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ public class WildcardArgumentConstraint implements IArgumentConstraint {

private WildcardArgumentConstraint() {}

@Override
public boolean isDeclarationEqualTo(IArgumentConstraint other) {
return this == other;
}

@Override
public boolean isSatisfiedBy(Object arg) {
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ public class WildcardMethodNameConstraint implements IInvocationConstraint {

private WildcardMethodNameConstraint() {}

@Override
public boolean isDeclarationEqualTo(IInvocationConstraint other) {
return this == other;
}

@Override
public boolean isSatisfiedBy(IMockInvocation invocation) {
return DefaultJavaLangObjectInteractions.INSTANCE.match(invocation) == null;
Expand Down
Loading

0 comments on commit a728e3c

Please sign in to comment.