ScampiSourceScampiShould

class Should

The assertion wrapper returned by Object#should.

Supports chainable assertions like .should.be.empty, .should.not == 5, and .should.be.a(matcher). Undefines predicate and operator methods from Object so they can be intercepted via method_missing.

Definitions

instance_methods.each { |name| undef_method name if name =~ /\?|^\W+$/ }

Undefine predicate and operator methods so they route through method_missing.

def initialize(object)

Wrap an object for assertion.

Signature

parameter object Object

The value under test.

Implementation

def initialize(object)
  @object = object
  @negated = false
end

def not(*args, &block)

Toggle negation. Can be chained: .should.not.be.empty.

Signature

returns Should

self, for chaining.

Implementation

def not(*args, &block)
  @negated = !@negated

  if args.empty?
    self
  else
    be(*args, &block)
  end
end

def be(*args, &block)

Identity chain or custom matcher. With no arguments, returns self for further chaining. With a block or lambda, delegates to satisfy.

Implementation

def be(*args, &block)
  if args.empty?
    self
  else
    unless block_given?
      block = args.shift
    end
    satisfy(*args, &block)
  end
end

def satisfy(description="", &block)

Assert that the block returns truthy for the wrapped object.

Signature

parameter description String

Failure message.

returns Boolean

Implementation

def satisfy(description="", &block)
  r = yield(@object)
  if Scampi::Counter[:depth] > 0
    Scampi::Counter[:requirements] += 1
    raise Scampi::Error.new(:failed, description)  unless @negated ^ r
    r
  else
    @negated ? !r : !!r
  end
end

def method_missing(name, *args, &block)

Catch predicate calls and auto-append ? to the method name. For example, .should.be.empty becomes .empty? on the object.

Implementation

def method_missing(name, *args, &block)
  name = "#{name}?"  if name.to_s =~ /\w[^?]\z/

  desc = @negated ? "not ".dup : "".dup
  desc << @object.inspect << "." << name.to_s
  desc << "(" << args.map{|x|x.inspect}.join(", ") << ") failed"

  satisfy(desc) { |x| x.__send__(name, *args, &block) }
end

def equal(value)

Assert equality using ==.

Signature

parameter value Object

def match(value)

Assert the object matches a pattern using =~.

Signature

parameter value Regexp

def identical_to(value)

Assert object identity (same object in memory).

Signature

parameter value Object

def flunk(reason="Flunked")

Unconditionally fail the current spec.

Signature

parameter reason String

Failure message.

Implementation

def flunk(reason="Flunked")
  raise Scampi::Error.new(:failed, reason)
end