RSpec

Library providing tools for writing and running RSpec tests.

Module: RSpec::SleepingKingStudios::Concerns::IncludeContract

Parent Namespace
RSpec::SleepingKingStudios::Concerns
Defined In
lib/rspec/sleeping_king_studios/concerns/include_contract.rb

Table Of Contents

Overview

Defines helpers for including reusable contracts in RSpec example groups.

RSpec contracts are a mechanism for sharing tests between projects. For example, one library may define an interface or specification for a type of object, while a second library implements that object. By defining a contract and sharing that contract as part of the library, the developer ensures that any object that matches the contract has correctly implemented and conforms to the interface. This reduces duplication of tests and provides resiliency as an interface is developed over time and across versions of the library.

Mechanically speaking, each contract encapsulates a section of RSpec code. When the contract is included in a spec, that code is then injected into the spec. Writing a contract, therefore, is no different than writing any other RSpec specification - it is only the delivery mechanism that differs. A contract can be any object that responds to #to_proc; the simplest contract is therefore a Proc or lambda that contains some RSpec code.

Examples

Defining A Contract

module ExampleContracts
  # This contract asserts that the object has the Enumerable module as an
  # ancestor, and that it responds to the #each method.
  SHOULD_BE_ENUMERABLE_CONTRACT = lambda do
    it 'should be Enumerable' do
      expect(subject).to be_a Enumerable
    end

    it 'should respond to #each' do
      expect(subject).to respond_to(:each).with(0).arguments
    end
  end
end

RSpec.describe Array do
  extend RSpec::SleepingKingStudios::Concerns::IncludeContract

  include_contract ExampleContracts::SHOULD_BE_ENUMERABLE_CONTRACT
end

RSpec.describe Hash do
  extend  RSpec::SleepingKingStudios::Concerns::IncludeContract
  include ExampleContracts

  include_contract 'should be enumerable'
end

Defining A Contract With Parameters

module SerializerContracts
  # This contract asserts that the serialized result has the expected
  # values.
  #
  # First, we pass the contract a series of attribute names. These are
  # used to assert that the serialized attributes match the values on the
  # original object.
  #
  # Second, we pass the contract a set of attribute names and values.
  # These are used to assert that the serialized attributes have the
  # specified values.
  #
  # Finally, we can pass the contract a block, which the contract then
  # executes. Note that the block is executed in the context of our
  # describe block, and thus can take advantage of our memoized
  # #serialized helper method.
  SHOULD_SERIALIZE_ATTRIBUTES_CONTRACT = lambda \
  do |*attributes, **values, &block|
    describe '#serialize' do
      let(:serialized) { subject.serialize }

      it { expect(subject).to respond_to(:serialize).with(0).arguments }

      attributes.each do |attribute|
        it "should serialize #{attribute}" do
          expect(serialized[attribute]).to be == subject[attribute]
        end
      end

      values.each do |attribute, value|
        it "should serialize #{attribute}" do
          expect(serialized[attribute]).to be == value
        end
      end

      instance_exec(&block) if block
    end
  end

RSpec.describe CaptainPicard do
  extend  RSpec::SleepingKingStudios::Concerns::IncludeContract
  include SerializerContracts

  include_contract 'should serialize attributes',
    :name,
    :rank,
    lights: 4 do
      it 'should serialize the catchphrase' do
        expect(serialized[:catchphrase]).to be == 'Make it so.'
      end
  end
end

See Also

Back To Top

Instance Methods

Missing Method: r-spec/sleeping-king-studios/concerns/include-contract/i-finclude-contract @ 2.8

#include_contract(contract, *arguments, **keywords, &block) => Object
#include_contract(contract_name, *arguments, **keywords, &block) => Object

Adds the contract to the example group with the given parameters.

Overloads

#include_contract(contract, *arguments, **keywords, &block) => Object
Parameters
  • contract (#to_proc) — The contract to include.
  • arguments (Array) — The arguments to pass to the contract.
  • keywords (Hash) — The keywords to pass to the contract.
Yields
  • A block passed to the contract.
#include_contract(contract_name, *arguments, **keywords, &block) => Object
Parameters
  • contract_name (String, Symbol) — The name of contract to include. The contract must be defined as a Class or constant in the same scope, e.g. include_contract('does something') expects the example group to define either a DoSomething class or a DO_SOMETHING constant. The name can optionally be suffixed with "contract", so it will also match a DoSomethingContract class or a DO_SOMETHING_CONTRACT constant.
  • arguments (Array) — The arguments to pass to the contract.
  • keywords (Hash) — The keywords to pass to the contract.
Yields
  • A block passed to the contract.
Raises
  • () — ArgumentError

Missing Method: r-spec/sleeping-king-studios/concerns/include-contract/i-xinclude-contract @ 2.8

Back To Top


Back to Documentation | Versions | 2.8 | Reference | RSpec | RSpec::SleepingKingStudios | RSpec::SleepingKingStudios::Concerns