Class: AE::Assertor
- Inherits:
- BasicObject
- Includes:
- Subjunctive
- Defined in:
- lib/ae/assertor.rb,
lib/ae/subjunctive.rb,
lib/ae/adapters/testunit.rb
Overview
:nodoc:
Constant Summary collapse
- ZERO_COUNTS =
Initial settings of assertion counts.
{:total=>0,:pass=>0,:fail=>0}
- COMPARISON_OPERATORS =
{ :"==" => :"!=" }
Class Method Summary collapse
-
.assert(pass, error = nil, negated = nil, backtrace = nil) ⇒ Object
Basic assertion.
-
.assertion_error ⇒ Object
Returns the Exception class to be raised when an assertion fails.
- .const_missing(const) ⇒ Object private
-
.counts ⇒ Object
Returns Hash used to track assertion counts.
-
.increment_counts(pass) ⇒ Object
Increment assertion counts.
-
.raise_assertion(error, negated, backtrace = nil) ⇒ Object
The intent of the method is to raise an assertion failure class that the test framework supports.
-
.recount(reset = nil) ⇒ Object
Reset assertion counts.
Instance Method Summary collapse
-
#=~(match) ⇒ Object
Ruby seems to have a quark in it’s implementation whereby this must be defined explicitly, otherwise it somehow skips #method_missing.
-
#__assert__(pass, error = nil) ⇒ Object
private
Simple assert.
-
#assay_assertion?(assertion) ⇒ Boolean
private
Is the ‘assertion` object an assay-style assertion?.
- #assay_assertion_apply(assay) ⇒ Object private
-
#assert(*args, &block) ⇒ Object
Internal assert, provides all functionality associated with external #assert method.
-
#compare_message(operator, *args, &blk) ⇒ Object
private
Message to use when making a comparion assertion.
-
#exception?(object) ⇒ Boolean
private
Is the
object
an Exception or an instance of one?. -
#expect(*args, &block) ⇒ Object
Internal expect, provides all functionality associated with external #expect method.
- #flunk(message = nil, backtrace = nil) ⇒ Object
-
#generic_message(op, *a, &b) ⇒ String
private
Puts together a suitable error message.
-
#initialize(delegate, opts = {}) ⇒ Assertor
constructor
New Assertor.
- #inspect ⇒ Object
-
#method_missing(sym, *args, &block) ⇒ Object
private
Converts a missing method into an Assertion.
-
#not(msg = nil) ⇒ Object
Negate the meaning of the assertion.
- #proc_apply(target) ⇒ Object private
- #proc_assertion?(target) ⇒ Boolean private
-
#rspec_matcher?(target) ⇒ Boolean
private
Is ‘target` an Rspec-style Matcher?.
- #rspec_matcher_apply(matcher) ⇒ Object private
- #rspec_matcher_message(matcher) ⇒ Object private
- #send(op, *a, &b) ⇒ Object
Methods included from Subjunctive
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 || (sym, *args, &block) || (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_error ⇒ Object
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 (private)
415 416 417 |
# File 'lib/ae/assertor.rb', line 415 def self.const_missing(const) ::Object.const_get(const) end |
.counts ⇒ Object
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
Increment assertion counts. If pass
is true
then :total
and :pass
are increased. If pass
if false
then :total
and :fail
are incremented.
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__(pass, error = nil) ⇒ Object (private)
Simple assert.
357 358 359 |
# File 'lib/ae/assertor.rb', line 357 def __assert__(pass, error=nil) Assertor.assert(pass, error, @negated, @backtrace) end |
#assay_assertion?(assertion) ⇒ Boolean (private)
Is the ‘assertion` object an assay-style assertion?
282 283 284 |
# File 'lib/ae/assertor.rb', line 282 def assay_assertion?(assertion) assertion.respond_to?(:exception) && assertion.respond_to?(:pass?) end |
#assay_assertion_apply(assay) ⇒ Object (private)
287 288 289 290 291 292 293 294 295 296 |
# File 'lib/ae/assertor.rb', line 287 def assay_assertion_apply(assay) if @negated pass = assay.fail?(@delegate) error = assay #.exception(@message || ) else pass = assay.pass?(@delegate) error = assay #.exception(@message || ) end return pass, error 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 |
#compare_message(operator, *args, &blk) ⇒ Object (private)
Message to use when making a comparion assertion.
NOTE: This message utilizes the ANSI gem to produce colorized comparisons. If you need to remove color output (for non-ANSI terminals) you can either set ‘AE.ansi = false` or use the ANSI library’s master switch to deactive all ANSI codes, which can be set in your test helper.
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 |
# File 'lib/ae/assertor.rb', line 375 def (operator, *args, &blk) return nil unless COMPARISON_OPERATORS.key?(operator) prefix = "" a, b = @delegate.inspect, args.first.inspect if @negated op = COMPARISON_OPERATORS[operator] if op operator = op else prefix = "NOT " end end if AE.ansi? diff = ::ANSI::Diff.new(a,b) a = diff.diff1 b = diff.diff2 end if a.size > 13 or b.size > 13 prefix + "a #{operator} b\na) " + a + "\nb) " + b else prefix + "#{a} #{operator} #{b}" end end |
#exception?(object) ⇒ Boolean (private)
Is the object
an Exception or an instance of one?
339 340 341 |
# File 'lib/ae/assertor.rb', line 339 def exception?(object) ::Exception === object or ::Class === object and object.ancestors.include?(::Exception) 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(=nil, backtrace=nil) __assert__(false, || @message) end |
#generic_message(op, *a, &b) ⇒ String (private)
Puts together a suitable error message.
404 405 406 407 408 409 410 411 412 |
# File 'lib/ae/assertor.rb', line 404 def (op, *a, &b) inspection = @delegate.send(:inspect) if @negated "! #{inspection} #{op} #{a.collect{|x| x.inspect}.join(',')}" else "#{inspection} #{op} #{a.collect{|x| x.inspect}.join(',')}" end #self.class.message(m)[@delegate, *a] ) end |
#inspect ⇒ Object
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 |
#proc_apply(target) ⇒ Object (private)
268 269 270 271 272 273 274 275 276 |
# File 'lib/ae/assertor.rb', line 268 def proc_apply(target) call = target.method(:call) rescue target.to_proc pass = call.arity != 0 ? call.call(@delegate) : call.call error = @message || ( to_s = target.method(:to_s) to_s.arity == 0 ? to_s.call : to_s.call(@negated) ) return pass, error end |
#proc_assertion?(target) ⇒ Boolean (private)
263 264 265 |
# File 'lib/ae/assertor.rb', line 263 def proc_assertion?(target) ::Proc === target || target.respond_to?(:call) || target.respond_to?(:to_proc) end |
#rspec_matcher?(target) ⇒ Boolean (private)
Is ‘target` an Rspec-style Matcher?
301 302 303 |
# File 'lib/ae/assertor.rb', line 301 def rspec_matcher?(target) target.respond_to?(:matches?) end |
#rspec_matcher_apply(matcher) ⇒ Object (private)
306 307 308 309 310 |
# File 'lib/ae/assertor.rb', line 306 def rspec_matcher_apply(matcher) pass = matcher.matches?(@delegate) error = @message || (matcher) return pass, error end |
#rspec_matcher_message(matcher) ⇒ Object (private)
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/ae/assertor.rb', line 315 def (matcher) if @negated if matcher.respond_to?(:failure_message_for_should_not) return matcher. end if matcher.respond_to?(:negative_failure_message) return matcher. end end if matcher.respond_to?(:failure_message_for_should) return matcher. end if matcher.respond_to?(:failure_message) return matcher. end return matcher.to_s # TODO: or just `nil` ? 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 |