Class: AE::Assertor

Inherits:
BasicObject
Includes:
Subjunctive
Defined in:
lib/ae/assertor.rb,
lib/ae/subjunctive.rb,
lib/ae/adapters/minitest.rb,
lib/ae/adapters/testunit.rb

Overview

:nodoc:

Constant Summary collapse

ZERO_COUNTS =

Initial settings of assertion counts.

{:total=>0,:pass=>0,:fail=>0}

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Subjunctive

#a, #be

Methods inherited from BasicObject

find_hidden_method, hide, reveal

Constructor Details

#initialize(delegate, opts = {}) ⇒ Assertor

New Assertor.



111
112
113
114
115
116
# File 'lib/ae/assertor.rb', line 111

def initialize(delegate, opts={}) #, backtrace)
  @delegate  = delegate
  @message   = opts[:message]
  @backtrace = opts[:backtrace] || caller #[1..-1]
  @negated   = !!opts[:negated]
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(sym, *args, &block) ⇒ Object (private)

Converts a missing method into an Assertion.



346
347
348
349
350
351
352
# File 'lib/ae/assertor.rb', line 346

def method_missing(sym, *args, &block)
  error = @message || compare_message(sym, *args, &block) || generic_message(sym, *args, &block)

  pass = @delegate.__send__(sym, *args, &block)

  __assert__(pass, error)
end

Class Method Details

.assert(pass, error = nil, negated = nil, backtrace = nil) ⇒ Object

Basic assertion. This method by-passes all the Assertor fluent constructs and performs the underlying assertion procedure. It is used by Assertor as the end call of an assertion.



62
63
64
65
66
67
68
69
70
# File 'lib/ae/assertor.rb', line 62

def self.assert(pass, error=nil, negated=nil, backtrace=nil)
  pass = negated ^ !!pass
  increment_counts(pass)
  if !pass
    backtrace = backtrace || caller
    raise_assertion(error, negated, backtrace)
  end
  return pass
end

.assertion_errorObject

Returns the Exception class to be raised when an assertion fails.



85
86
87
# File 'lib/ae/assertor.rb', line 85

def self.assertion_error
  ::Assertion
end

.const_missing(const) ⇒ Object



415
416
417
# File 'lib/ae/assertor.rb', line 415

def self.const_missing(const)
 ::Object.const_get(const)
end

.countsObject

Returns Hash used to track assertion counts.



25
26
27
# File 'lib/ae/assertor.rb', line 25

def self.counts
  $ASSERTION_COUNTS
end

.increment_counts(pass) ⇒ Object

MiniTest tracks assertion counts internally. To work with AE we override increment_counts to also update AE's own counter.



49
50
51
52
53
54
55
56
57
# File 'lib/ae/assertor.rb', line 49

def self.increment_counts(pass)
  counts[:total] += 1
  if pass
    counts[:pass] += 1
  else
    counts[:fail] += 1
  end
  return counts
end

.raise_assertion(error, negated, backtrace = nil) ⇒ Object

The intent of the method is to raise an assertion failure class that the test framework supports.



74
75
76
77
78
79
80
81
82
# File 'lib/ae/assertor.rb', line 74

def self.raise_assertion(error, negated, backtrace=nil)
  if not ::Exception === error
    error = assertion_error.new(error)
  end
  error.set_negative(negated)
  error.set_backtrace(backtrace || caller)
  error.set_assertion(true)
  fail error
end

.recount(reset = nil) ⇒ Object

Reset assertion counts.

reset - Hash which can be used to set counts manually (optional).

Returns the Hash of previous counts.



34
35
36
37
38
39
40
41
42
43
44
# File 'lib/ae/assertor.rb', line 34

def self.recount(reset=nil)
  old_counts = counts.dup
  if reset
    reset.each do |type, value|
      counts[type.to_sym] = value
    end
  else
    counts.replace(ZERO_COUNTS.dup)
  end
  return old_counts
end

Instance Method Details

#=~(match) ⇒ Object

Ruby seems to have a quark in it's implementation whereby this must be defined explicitly, otherwise it somehow skips #method_missing.



244
245
246
# File 'lib/ae/assertor.rb', line 244

def =~(match)
  method_missing(:"=~", match)
end

#assert(*args, &block) ⇒ Object

Internal assert, provides all functionality associated with external #assert method. (See Assert#assert)

NOTE: I'm calling YAGNI on using extra arguments to pass to the block. The interface is much nicer if a macro is created to handle any neccessry arguments. Eg.

assert something(parameter)

instead of

assert something, parameter

Returns true or false based on assertions success.



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/ae/assertor.rb', line 143

def assert(*args, &block)
  return self if !block && args.empty?

  target = args.shift unless block
  error  = nil

  # Block
  if block
    match  = args.shift
    result = block.arity > 0 ? block.call(@delegate) : block.call
    if match
      pass  = (match == result)
      error = @message || "#{match.inspect} == #{result.inspect}"
    else
      pass  = result
      error = @message || block.inspect  # "#{result.inspect}"
    end

  # Proc-style
  elsif proc_assertion?(target)
    pass, error = proc_apply(target)

  # Assay-style assertions
  #elsif assay_assertion?(target)
  #  pass, error = assay_assertion_apply(target)

  # RSpec-style matchers
  elsif rspec_matcher?(target)
    pass, error = rspec_matcher_apply(target)

  # Truthiness
  else
    pass  = target     # truthiness
    error = args.shift # optional message for TestUnit compatiability
  end

  __assert__(pass, error)
end

#expect(*args, &block) ⇒ Object

Internal expect, provides all functionality associated with external #expect method. (See Expect#expect)



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/ae/assertor.rb', line 188

def expect(*args, &block)
  return self if !block && args.empty?  # same as #assert

  pass  = false
  error = nil

  if block
    match = args.shift || @delegate  # TODO: see above
    if exception?(match)
      $DEBUG, debug = false, $DEBUG  # b/c it always spits-out a NameError
      begin
        block.arity > 0 ? block.call(@delegate) : block.call
        pass  = false
        error = "#{match} not raised"
      rescue match => error
        pass  = true
        error = "#{match} raised"
      rescue ::Exception => error
        pass  = false
        error = "#{match} expected but #{error.class} was raised"
      ensure
        $DEBUG = debug
      end
    else
      result = block.arity > 0 ? block.call(@delegte) : block.call
      pass   = (match === result)
      error  = @message || "#{match.inspect} === #{result.inspect}"
    end

  ## Matcher
  #elsif target.respond_to?(:matches?)
  #  pass  = target.matches?(@delegate)
  #  error = @message || matcher_message(target) #|| target.inspect
  #  if target.respond_to?(:exception)
  #    #error_class = target.failure_class
  #    error = target.exception #failure(:backtrace=>@backtrace, :negated=>@negated)
  #  end

  # Case Equality
  else
    target = args.shift
    pass   = (target === @delegate)
    error  = @message || "#{target.inspect} === #{@delegate.inspect}"
  end

  __assert__(pass, error)
end

#flunk(message = nil, backtrace = nil) ⇒ Object



237
238
239
# File 'lib/ae/assertor.rb', line 237

def flunk(message=nil, backtrace=nil)
  __assert__(false, message || @message)
end

#inspectObject



254
255
256
# File 'lib/ae/assertor.rb', line 254

def inspect
  @delegate.inspect
end

#not(msg = nil) ⇒ Object

Negate the meaning of the assertion.



122
123
124
125
126
# File 'lib/ae/assertor.rb', line 122

def not(msg=nil)
  @negated = !@negated
  @message = msg if msg
  self
end

#send(op, *a, &b) ⇒ Object



249
250
251
# File 'lib/ae/assertor.rb', line 249

def send(op, *a, &b)
  method_missing(op, *a, &b)
end