spock.lang.Specification Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spock-core Show documentation
Show all versions of spock-core Show documentation
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.
/*
* Copyright 2009 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 org.spockframework.lang.Wildcard;
import org.spockframework.runtime.*;
import org.spockframework.util.*;
import spock.mock.MockingApi;
import groovy.lang.*;
import groovy.transform.stc.*;
import org.junit.platform.commons.annotation.Testable;
/**
* 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 extends Throwable> 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);
}
}