Module: BareTest::Assertion::Support

Included in:
Context
Defined in:
lib/baretest/assertion/support.rb

Overview

BareTest::Assertion::Support is per default included into BareTest::Assertion. It provides several methods to make it easier to write assertions.

Instance Method Summary collapse

Instance Method Details

#case_equal(*args) ⇒ Object

Uses === to test whether the objects are equal

Can be used in either of the following ways:

equal expected, actual
equal :expected => expected, :actual => actual


263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/baretest/assertion/support.rb', line 263

def case_equal(*args)
  expected, actual, message = extract_args(args, :expected, :actual, :message)

  unless expected === actual then
    failure_with_optional_message \
      "Expected %s to be case equal (===) to %p but was %p",
      "Expected %p but got %p",
      message, expected, actual
  end
  true

rescue ::BareTest::Assertion::Failure, *::BareTest::Assertion::PassthroughExceptions
  ::Kernel.raise
rescue Exception => e
  failure "Could not compare %p with %p due to %s", expected, actual, e
end

#equal_unordered(*args) ⇒ Object

To compare two collections (which must implement #each) without considering order. E.g. two sets, or the keys of two hashes.



283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
# File 'lib/baretest/assertion/support.rb', line 283

def equal_unordered(*args)
  expected, actual, message = extract_args(args, :expected, :actual, :message)

  count = Hash.new(0)
  expected.each { |element| count[element] += 1 }
  actual.each   { |element| count[element] -= 1 }

  unless count.all? { |key, value| value.zero? } then
    only_in_expected = count.select { |ele, n| n > 0 }.map { |ele, n| ele }
    only_in_actual   = count.select { |ele, n| n < 0 }.map { |ele, n| ele }
    if message then
      failure "Expected %s to have the same items the same number of times, " \
              "but %p are only in expected, and %p only in actual",
              message, only_in_expected, only_in_actual
    else
      failure "Expected %p and %p to have the same items the same number of times, " \
              "but %p are only in expected, and %p only in actual",
              expected, actual, only_in_expected, only_in_actual
    end
  end
  true

rescue ::BareTest::Assertion::Failure, *::BareTest::Assertion::PassthroughExceptions
  ::Kernel.raise
rescue Exception => e
  failure "Could not compare %p with %p due to %s", expected, actual, e
end

#extract_args(args, *named) ⇒ Object

extract arg allows to use named or positional args

Example:

extract_args([1,2,3], :foo, :bar, :baz) # => [1,2,3]
extract_args({:foo => 1,:bar => 2, :baz => 3}, :foo, :bar, :baz) # => [1,2,3]

Usage:

def foo(*args)
  x,y,z = extract_args(args, :x, :y, :z)
end
foo(1,2,3)
foo(:x => 1, :y => 2, :z => 3) # equivalent to the one above


400
401
402
403
404
405
406
# File 'lib/baretest/assertion/support.rb', line 400

def extract_args(args, *named)
  if args.size == 1 && Hash === args.first then
    args.first.values_at(*named)
  else
    args.first(named.size)
  end
end

#failure(message = "Assertion failed", *args) ⇒ Object

Raises Test::Assertion::Failure, which causes the Assertion to get the status :failure. Runs sprintf over message with *args Particularly useful with %p and %s.



376
377
378
# File 'lib/baretest/assertion/support.rb', line 376

def failure(message="Assertion failed", *args)
  raise ::BareTest::Assertion::Failure, sprintf(message, *args)
end

#failure_with_optional_message(with_message, without_message, message, *args) ⇒ Object

A method to make raising failures that only optionally have a message easier.



365
366
367
368
369
370
371
# File 'lib/baretest/assertion/support.rb', line 365

def failure_with_optional_message(with_message, without_message, message, *args)
  if message then
    failure(with_message, message, *args)
  else
    failure(without_message, *args)
  end
end

#hash_key_equal(*args) ⇒ Object

Uses eql? to test whether the objects are equal

Can be used in either of the following ways:

equal expected, actual
equal :expected => expected, :actual => actual


216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/baretest/assertion/support.rb', line 216

def hash_key_equal(*args)
  expected, actual, message = extract_args(args, :expected, :actual, :message)

  unless expected.eql?(actual) then
    if message then
      failure "Expected %s to be hash-key equal (eql?) to %p but was %p", message, expected, actual
    else
      failure "Expected %p but got %p", expected, actual
    end
  end
  true

rescue ::BareTest::Assertion::Failure, *::BareTest::Assertion::PassthroughExceptions
  ::Kernel.raise
rescue Exception => e
  failure "Could not compare %p with %p due to %s", expected, actual, e
end

#instance_of(*args) ⇒ Object

Raises a Failure if the given object is not an instance of the given class



330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
# File 'lib/baretest/assertion/support.rb', line 330

def instance_of(*args)
  expected, actual, message = extract_args(args, :expected, :actual, :message)
  unless actual.instance_of?(expected) then
    failure_with_optional_message \
      "Expected %1$s to be an instance of %3$p, but was a %4$p",
      "Expected %2$p to be an instance of %1$p, but was a %3$p",
      message, expected, actual, actual.class
  end
  true

rescue ::BareTest::Assertion::Failure, *::BareTest::Assertion::PassthroughExceptions
  ::Kernel.raise
rescue Exception => e
  failure "Could not test whether %p is an instance of %p due to %s", actual, expected, e
end

#kind_of(*args) ⇒ Object

Raises a Failure if the given object is not an instance of the given class or a descendant thereof



313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/baretest/assertion/support.rb', line 313

def kind_of(*args)
  expected, actual, message = extract_args(args, :expected, :actual, :message)
  unless actual.kind_of?(expected) then
    failure_with_optional_message \
      "Expected %1$s to be a kind of %3$p, but was a %4$p",
      "Expected %2$p to be a kind of %1$p, but was a %3$p",
      message, expected, actual, actual.class
  end
  true

rescue ::BareTest::Assertion::Failure, *::BareTest::Assertion::PassthroughExceptions
  ::Kernel.raise
rescue Exception => e
  failure "Could not test whether %p is a kind of %p due to %s", actual, expected, e
end

#not_touched(thing = nil) ⇒ Object

Used to verify that something was not touched.

See #touch



184
185
186
# File 'lib/baretest/assertion/support.rb', line 184

def not_touched(thing=nil)
  touched(thing, 0)
end

#order_equal(*args) ⇒ Object Also known as: equal

Uses == to test whether the objects are equal

Can be used in either of the following ways:

equal expected, actual
equal :expected => expected, :actual => actual


239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/baretest/assertion/support.rb', line 239

def order_equal(*args)
  expected, actual, message = extract_args(args, :expected, :actual, :message)

  unless expected == actual then
    if message then
      failure "Expected %s to be order equal (==) to %p but was %p", message, expected, actual
    else
      failure "Expected %p but got %p", expected, actual
    end
  end
  true

rescue ::BareTest::Assertion::Failure, *::BareTest::Assertion::PassthroughExceptions
  ::Kernel.raise
rescue Exception => e
  failure "Could not compare %p with %p due to %s", expected, actual, e
end

#raises(expected_exception_class = nil, with_message = nil, opts = {}) ⇒ Object

Will raise a Failure if the given block doesn’t raise or raises a different exception than the one provided You can optionally give an options :with_message, which is tested with === against the exception message.

Examples:

raises do raise "will work" end # => true
raises SomeException do raise SomeException end # => true
raises :with_message => "bar" do raise "bar" end # => true
raises SomeException, :with_message => "bar"; raise SomeException, "bar" end # => true
raises :with_message => /\Aknown \w+\z/; raise "known unknown" end # => true


93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/baretest/assertion/support.rb', line 93

def raises(expected_exception_class=nil, with_message=nil, opts={})
  exception_class = expected_exception_class || StandardError
  yield
rescue exception_class => exception
  if expected_exception_class && exception.class != expected_exception_class then
    failure "Expected the code to raise #{expected_exception_class}, but it raised #{exception.class} instead"
  elsif with_message && !(with_message === exception.message) then
    failure "Expected the code to raise with the message %p, but the message was %p",
            with_message, exception.message
  else
    true
  end
rescue ::BareTest::Assertion::Failure, *::BareTest::Assertion::PassthroughExceptions
  ::Kernel.raise
rescue => exception
  failure "Expected the code to raise #{expected_exception_class}, but it raised #{exception.class} instead"
else
  if expected_exception_class then
    failure "Expected the code to raise #{expected_exception_class}, but nothing was raised"
  else
    failure "Expected the code to raise, but nothing was raised"
  end
end

#raises_nothingObject

Will raise a Failure if the given block raises.



118
119
120
121
122
123
124
125
126
# File 'lib/baretest/assertion/support.rb', line 118

def raises_nothing
  yield
rescue ::BareTest::Assertion::Failure, *::BareTest::Assertion::PassthroughExceptions
  ::Kernel.raise
rescue Exception => exception
  failure "Expected the code to raise nothing, but it raised #{exception.class} (#{exception.message})"
else
  true
end

#respond_to(obj, *methods) ⇒ Object

Raises a Failure if the given object does not respond to all of the given method names. The method names may be specified as String or Symbol.



348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
# File 'lib/baretest/assertion/support.rb', line 348

def respond_to(obj, *methods)
  not_responded_to = methods.reject { |method_name| obj.respond_to?(method_name) }
  unless not_responded_to.empty? then
    must_respond_to  = methods.map { |m| m.to_sym.inspect }.join(', ')
    not_responded_to = not_responded_to.map { |m| m.to_sym.inspect }.join(', ')
    failure "Expected %1$s to respond to all of %2$s, but it did not respond to %3$s",
       obj, must_respond_to, not_responded_to
  end
  true

rescue ::BareTest::Assertion::Failure, *::BareTest::Assertion::PassthroughExceptions
  ::Kernel.raise
rescue Exception => e
  failure "Could not test whether %p responds to %p due to %s", obj, methods, e
end

#same(*args) ⇒ Object

Uses equal? to test whether the objects are the same

Can be used in either of the following ways:

same expected, actual
same :expected => expected, :actual => actual


193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/baretest/assertion/support.rb', line 193

def same(*args)
  expected, actual, message = extract_args(args, :expected, :actual, :message)

  unless expected.equal?(actual) then
    if message then
      failure "Expected %s to be the same (equal?) as %p but was %p", message, expected, actual
    else
      failure "Expected %p but got %p", expected, actual
    end
  end
  true

rescue ::BareTest::Assertion::Failure, *::BareTest::Assertion::PassthroughExceptions
  ::Kernel.raise
rescue Exception => e
  failure "Could not compare %p with %p due to %s", expected, actual, e
end

#skip(message = "Assertion was skipped", *args) ⇒ Object

Raises Test::Assertion::Skip, which causes the Assertion to get the status :skipped. Runs sprintf over message with *args Particularly useful with %p and %s.



383
384
385
# File 'lib/baretest/assertion/support.rb', line 383

def skip(message="Assertion was skipped", *args)
  raise ::BareTest::Assertion::Skip, sprintf(message, *args)
end

#throws(symbol) ⇒ Object

FIXME: incomplete and untested



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/baretest/assertion/support.rb', line 57

def throws(symbol) # :nodoc:
  passed = false
  catch(symbol) {
    yield
    failure "Expected the code to throw %p, but nothing was thrown", symbol
  }
  return true
# throw raises a NameError if no catch with appropriate symbol is set up
rescue ArgumentError, NameError => e
  # Make sure it's not a NameError with a different reason than the throw
  # ruby 1.8.7: NameError, "uncaught throw `symbol'"
  # ruby 1.9.1: ArgumentError, "uncaught throw :symbol"
  threw_instead = e.message[/\Auncaught throw `(.*)'\z/, 1] || e.message[/\Auncaught throw :(.*)\z/, 1]
  if threw_instead then
    failure "Expected the code to throw %p, but it threw %p instead", symbol, threw_instead.to_sym
  else
    # It was some other name error, reraise
    raise
  end
end

#throws_nothingObject

FIXME: incomplete and untested



79
80
# File 'lib/baretest/assertion/support.rb', line 79

def throws_nothing # :nodoc:
end

#touch(thing = nil) ⇒ Object

Use this method to test whether certain code (e.g. a callback) was reached. touch marks that it was reached, #touched tests for whether it was reached.

Example:

assert "Code in a Proc object is executed when invoking #call on it" do
  a_proc = proc { touch :executed }
  a_proc.call
  touched(:executed)
end


153
154
155
# File 'lib/baretest/assertion/support.rb', line 153

def touch(thing=nil)
  ::BareTest.touch(self, thing)
end

#touched(thing = nil, times = nil) ⇒ Object

Used to verify that something was touched. You can also verify that something was touched a specific amount of times.

See #touch



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/baretest/assertion/support.rb', line 161

def touched(thing=nil, times=nil)
  touched_times = ::BareTest.touched(self, thing)
  if times then
    unless touched_times == times then
      if thing then
        failure "Expected the code to touch %p %s times, but did %s times", thing, times, touched_times
      else
        failure "Expected the code to touch %s times, but did %s times", times, touched_times
      end
    end
  elsif touched_times < 1 then
    if thing then
      failure "Expected the code to touch %p, but it was not touched", thing
    else
      failure "Expected the code to touch, but no touch happened"
    end
  end
  true
end

#within_delta(a, b, delta) ⇒ Object

For comparisons of Floats you shouldn’t use == but for example a delta comparison instead, to take care of the possible rounding differences.



131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/baretest/assertion/support.rb', line 131

def within_delta(a, b, delta)
  actual_delta = (a-b).abs
  if actual_delta >= delta then
    failure "Expected %p and %p to differ less than %p, but they were different by %p", a, b, delta, actual_delta
  else
    true
  end
rescue ::BareTest::Assertion::Failure, *::BareTest::Assertion::PassthroughExceptions
  ::Kernel.raise
rescue Exception => e
  failure "Could not compare %p with %p due to %s", a, b, e
end

#yields(subject, meth, args, *expected) ⇒ Object

FIXME: undocumented and untested It’s really ugly. You should use a mock instead.



48
49
50
51
52
53
54
# File 'lib/baretest/assertion/support.rb', line 48

def yields(subject, meth, args, *expected)
  subject.__send__(meth, *args) do |*actual|
    current = expected.shift
    return false unless actual == current
  end
  return expected.empty?
end