module Mocks::Stubbable

Overview

Adds support for method stubs to a type.

This module is intended to be used as a mix-in. Include it to allow instance methods to be stubbed. Extend it to allow class methods to be stubbed.

Example usage:

class MyClass
  include Mocks::Stubbable

  stub the_answer = 42

  stub def stringify(arg) : String
    arg.to_s
  end
end

When defining stubbable methods, a behavior and type are needed. The behavior is a symbol that specifies how the method behaves without a user-defined stub. The behavior can be one of:

The type is needed so that values returned by stubs can be type checked and cast if necessary. It is also used to inform the compiler the method's type to avoid bloated unions. The type can be one of:

Direct including types

Defined in:

mocks/stubbable.cr

Constant Summary

UNSAFE_METHODS = [:allocate, :finalize, :initialize, :can] of ::Symbol

Names of methods to skip defining a stub for. These are typically special methods, such as Crystal built-ins, that would be unsafe to mock.

Class Method Summary

Macro Summary

Class Method Detail

def self.unexpected_method_call(method_name : Symbol, abstract_call : Bool, type : NoReturn.class) : NoReturn #

Raises an error that indicates an method was called unexpectedly.

Set abstract_call to true to change the error message to indicate an abstract method was "called".


[View source]
def self.unexpected_method_call(method_name : Symbol, abstract_call : Bool, type : T.class) : T forall T #

Raises an error that indicates an method was called unexpectedly.

The return type of this method matches the type passed in. Set abstract_call to true to change the error message to indicate an abstract method was "called".


[View source]

Macro Detail

macro stub(method) #

Defines a new stubbable method or redefines an existing method to support stubbing.

Multiple syntaxes are supported. The following define new methods that accept any arguments.

stub some_method1 : Int32
stub some_method2 : Int32 = 42
stub some_method3 = 42

When a stub isn't applied and a value is assigned, it is used as the return value. Otherwise, an UnexpectedMessage error is raised when the stubbed method is called.

A method (an its overrides) can be redefined by specifying the method name.

stub to_s

The argument list must be omitted. All methods matching the specified name will be redefined to support stubbing. When a stub isn't applied, the default behavior will be to call the original implementation.

Lastly, a method definition can be provided.

stub def do_something(arg) : String
  arg.to_s
end

This will define (or redefine) a method that supports stubbing. The method will only accept calls with a matching signature. When a stub isn't applied, the default behavior will be the contents of the method's body.

Abstract methods will be given an implementation that raises UnexpectedMessage. Provide a stub to define alternate behavior.

stub abstract def do_something
# ...
obj.do_something # Raises `UnexpectedMessage`
obj.can receive(:do_something)
obj.do_something # OK

NOTE Omitting the return type restriction for an abstract method will cause it to return nil. Specify a non-nil return type with a default stub, such as passing a block to this method, or by adding a return type restriction to the method.


[View source]