All Downloads are FREE. Search and download functionalities are using the official Maven repository.

spock.lang.Specification Maven / Gradle / Ivy

Go to download

Spock is a testing and specification framework for Java and Groovy applications. What makes it stand out from the crowd is its beautiful and highly expressive specification language. Thanks to its JUnit runner, Spock is compatible with most IDEs, build tools, and continuous integration servers. Spock is inspired from JUnit, jMock, RSpec, Groovy, Scala, Vulcans, and other fascinating life forms.

There is a newer version: 2.4-M4-groovy-4.0
Show newest version
/*
 * Copyright 2024 the original author or authors.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *      https://www.apache.org/licenses/LICENSE-2.0
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

package spock.lang;

import groovy.lang.Closure;
import groovy.lang.DelegatesTo;
import groovy.transform.stc.ClosureParams;
import groovy.transform.stc.FirstParam;
import groovy.transform.stc.SecondParam;
import org.junit.platform.commons.annotation.Testable;
import org.spockframework.lang.Wildcard;
import org.spockframework.runtime.*;
import org.spockframework.util.Beta;
import org.spockframework.util.ExceptionUtil;
import spock.mock.MockingApi;

import java.util.Objects;
import java.util.function.Function;

/**
 * Base class for Spock specifications. All specifications must inherit from
 * this class, either directly or indirectly.
 *
 * @author Peter Niederwieser
 */
@Testable
@SuppressWarnings("UnusedDeclaration")
public abstract class Specification extends MockingApi {
  /**
   * The wildcard symbol. Used in several places as a don't care value:
   * 
    *
  • Mock interactions
  • * Example: 1 * foo.bar(_) *
  • Data parameterizations
  • * Example: [foo, _] << loadDataFromDb() *
*/ public static final Object _ = Wildcard.INSTANCE; /** * Specifies that the preceding when block should throw an exception. * May only occur as the initializer expression of a typed variable declaration * in a then block; the expected exception type is inferred from the * variable type. *

This form of exception condition is typically used if the thrown * exception instance is used in subsequent conditions. * *

Example: *

   * when:
   * "".charAt(0)
   *
   * then:
   * IndexOutOfBoundsException e = thrown()
   * e.message.contains(...)
   * 
* * @return the thrown exception instance */ public T thrown() { throw new InvalidSpecException( "Exception conditions are only allowed in 'then' blocks, and may not be nested inside other elements"); } /** * Specifies that the preceding when block should throw an exception * of the given type. May only occur in a then block. *

This form of exception condition is typically used if the thrown * exception instance is not used in subsequent conditions. * *

Example: *

   * when:
   * "".charAt(0)
   *
   * then:
   * thrown(IndexOutOfBoundsException)
   *
   * @param type the expected exception type
   * @param  the expected exception type
   * @return the thrown exception instance
   */
  public  T thrown(Class type) {
    throw new InvalidSpecException(
        "Exception conditions are only allowed in 'then' blocks, and may not be nested inside other elements");
  }

  /**
   * Specifies that no exception of the given type should be
   * thrown, failing with a {@link UnallowedExceptionThrownError} otherwise.
   *
   * @param type the exception type that should not be thrown
   */
  public void notThrown(Class type) {
    Throwable thrown = getSpecificationContext().getThrownException();
    if (thrown == null) return;
    if (type.isAssignableFrom(thrown.getClass())) {
      throw new UnallowedExceptionThrownError(type, thrown);
    }
    ExceptionUtil.sneakyThrow(thrown);
  }

  /**
   * Specifies that no exception should be thrown, failing with a
   * {@link UnallowedExceptionThrownError} otherwise.
   */
  public void noExceptionThrown() {
    Throwable thrown = getSpecificationContext().getThrownException();
    if (thrown == null) return;
    throw new UnallowedExceptionThrownError(null, thrown);
  }

  /**
   * Used in a then-block to access an expression's value at the time just
   * before the previous where-block was entered.
   *
   * @param expression an arbitrary expression, except that it may not
   * reference variables defined in the then-block
   * @param  the expression's type
   * @return the expression's value at the time the previous where-block was
   * entered
   */
  public  T old(T expression) {
    throw new InvalidSpecException("old() can only be used in a 'then' block");
  }

  /**
   * Sets the specified object as the implicit target of the top-level conditions and/or
   * interactions contained in the specified code block, thereby avoiding the need to repeat
   * the same expression multiple times. Implicit conditions are supported. (In other words,
   * the {@code assert} keyword may be omitted.) If the target is {@code null}, a
   * {@code SpockAssertionError} is thrown.
   *
   * 

A {@code with} block can be used anywhere in a spec, including nested positions * and helper methods. * *

Condition example: * *

   * def fred = new Person(name: "Fred", age: 42)
   * def spaceship = new Spaceship(pilot: fred)
   *
   * expect:
   * with(spaceship.pilot) {
   *   name == "Fred" // shorthand for: spaceship.pilot.name == "Fred"
   *   age == 42
   * }
   * 
* *

Interaction example: * *

   * def service = Mock(Service) // has start(), stop(), and doWork() methods
   * def app = new Application(service) // controls the lifecycle of the service
   *
   * when:
   * app.run()
   *
   * then:
   * with(service) {
   *   1 * start() // shorthand for: 1 * service.start()
   *   1 * doWork()
   *   1 * stop()
   * }
   * 
* * @param target an implicit target for conditions and/or interactions * @param closure a code block containing top-level conditions and/or interactions * @param type of target */ @Beta public void with( @DelegatesTo.Target U target, @DelegatesTo(strategy = Closure.DELEGATE_FIRST) @ClosureParams(FirstParam.class) Closure closure ) { if (target == null) { throw new SpockAssertionError("Target of 'with' block must not be null"); } closure.setDelegate(target); // for conditions closure.setResolveStrategy(Closure.DELEGATE_FIRST); GroovyRuntimeUtil.invokeClosure(closure, target); } /** * Same as {@link #with(Object, groovy.lang.Closure)}, except that it also states that * the specified target has the specified type, throwing a {@code SpockAssertionError} * otherwise. As a side effect, this may give better code completion in IDEs. * *

Example: * *

   * def fred = new Employee(name: "Fred", age: 42, employer: "MarsTravelUnited")
   * def spaceship = new Spaceship(pilot: fred)
   *
   * expect:
   * with(spaceship.pilot, Employee) {
   *   name == "Fred" // shorthand for: spaceship.pilot.name == "Fred"
   *   age == 42
   *   employer == "MarsTravelUnited"
   * }
   * 
* * @param target an implicit target for conditions and/or interactions * @param type the expected type of the target * @param closure a code block containing top-level conditions and/or interactions * @param type of target */ @Beta public void with( Object target, @DelegatesTo.Target Class type, @DelegatesTo(genericTypeIndex = 0, strategy = Closure.DELEGATE_FIRST) @ClosureParams(SecondParam.FirstGenericType.class) Closure closure) { if (target != null && !type.isInstance(target)) { throw new SpockAssertionError(String.format("Expected target of 'with' block to have type '%s', but got '%s'", type, target.getClass().getName())); } with(target, closure); } /** * All assertions in this block are executed and the errors recorded and reported at the end. * *

Example: * *

   * expect:
   * verifyAll {
   *   1 == 2
   *   2 == 3
   * }
   * 
* * This will report two errors, instead of just the first. * * @param closure a code block containing top-level conditions and/or interactions */ @Beta public void verifyAll(Closure closure){ GroovyRuntimeUtil.invokeClosure(closure); } /** * A combination of {@link #with(Object, Closure)} and {@link #verifyAll(Closure)}. * * @since 1.2 * @param target an implicit target for conditions and/or interactions * @param closure a code block containing top-level conditions and/or interactions * @param type of target */ @Beta public void verifyAll( @DelegatesTo.Target U target, @DelegatesTo(strategy = Closure.DELEGATE_FIRST) @ClosureParams(FirstParam.class) Closure closure ){ if (target == null) { throw new SpockAssertionError("Target of 'verifyAll' block must not be null"); } closure.setDelegate(target); // for conditions closure.setResolveStrategy(Closure.DELEGATE_FIRST); GroovyRuntimeUtil.invokeClosure(closure, target); } /** * A combination of {@link #with(Object, Class, Closure)} and {@link #verifyAll(Closure)}. * * @since 1.2 * @param target an implicit target for conditions and/or interactions * @param type the expected type of the target * @param closure a code block containing top-level conditions and/or interactions * @param type of target */ @Beta public void verifyAll( Object target, @DelegatesTo.Target Class type, @DelegatesTo(genericTypeIndex = 0, strategy = Closure.DELEGATE_FIRST) @ClosureParams(SecondParam.FirstGenericType.class) Closure closure) { if (target != null && !type.isInstance(target)) { throw new SpockAssertionError(String.format("Expected target of 'verifyAll' block to have type '%s', but got '%s'", type, target.getClass().getName())); } verifyAll(target, closure); } /** * Performs assertions on each item, collecting up failures instead of stopping at first. *

* Exception messages will contain a toString() of the item to identify it. * * @param things the iterable to inspect * @param closure a code block containing top-level conditions * @param type of items in things * @since 2.4 */ @Beta public void verifyEach( Iterable things, @ClosureParams(value = FirstParam.FirstGenericType.class) @DelegatesTo(type = "U", strategy = Closure.DELEGATE_FIRST) Closure closure ) { verifyEach(things, Objects::toString, closure); } /** * Performs assertions on each item, collecting up failures instead of stopping at first. *

* Exception messages will contain the result of calling the namer for an item to identify it. * * @param things the iterable to inspect * @param namer the namer function to use when rendering the exception * @param closure a code block containing top-level conditions * @param type of items in things * @since 2.4 */ @Beta public void verifyEach( Iterable things, Function namer, @ClosureParams(value = FirstParam.FirstGenericType.class) @DelegatesTo(type = "U", strategy = Closure.DELEGATE_FIRST) Closure closure ) { if (things == null) { throw new SpockAssertionError("Target of 'verifyEach' block must not be null"); } if (namer == null) { throw new SpockAssertionError("Namer for a 'verifyEach' block must not be null"); } SpockRuntime.verifyEach(things, namer, closure); } }