Module: Detest

Extended by:
Detest
Included in:
Detest
Defined in:
lib/detest.rb,
lib/detest/long.rb,
lib/detest/mini.rb,
lib/detest/spec.rb,
lib/detest/unit.rb,
lib/detest/inochi.rb

Defined Under Namespace

Modules: FailureDetails Classes: BINDINGS=Hash.new{|h,k|h[k]={}}, Suite

Constant Summary collapse

Describe =

for hooks

D
PROJECT =

Official name of this project.

'Detest'
TAGLINE =

Short single-line description of this project.

'Assertion testing library for Ruby'
WEBSITE =

Address of this project’s official home page.

'http://snk.tuxfamily.org/lib/detest/'
VERSION =

Number of this release of this project.

'3.1.3'
RELDATE =

Date of this release of this project.

'2011-04-22'
INSTDIR =

Location of this release of this project.

File.expand_path('../../..', __FILE__)
GEMDEPS =

RubyGems required by this project during runtime.

Examples:


GEMDEPS = {
  # this project needs exactly version 1.2.3 of the "an_example" gem
  'an_example' => [ '1.2.3' ],

  # this project needs at least version 1.2 (but not
  # version 1.2.4 or newer) of the "another_example" gem
  'another_example' => [ '>= 1.2' , '< 1.2.4' ],

  # this project needs any version of the "yet_another_example" gem
  'yet_another_example' => [],
}
{}
D =

allow before and after hooks to be specified via the following method syntax when this module is mixed-in:

D .<< { puts "before all nested tests" }
D .<  { puts "before each nested test" }
D .>  { puts "after  each nested test" }
D .>> { puts "after  all nested tests" }
self

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Class Attribute Details

.debug=(value) ⇒ Object

Launch an interactive debugger during assertion failures so the user can investigate them?

The default value is $DEBUG.



31
32
33
# File 'lib/detest.rb', line 31

def debug=(value)
  @debug = value
end

.statsObject (readonly)

Hash of counts of major events in test execution:

:time

Number of seconds elapsed for test execution.

:pass

Number of assertions that held true.

:fail

Number of assertions that did not hold true.

:error

Number of exceptions that were not rescued.



48
49
50
# File 'lib/detest.rb', line 48

def stats
  @stats
end

.traceObject (readonly)

Hierarchical trace of all tests executed, where each test is represented by its description, is mapped to an Array of nested tests, and may contain zero or more assertion failures.

Assertion failures are represented as a Hash:

:fail

Description of the assertion failure.

:call

Stack trace leading to the point of failure.

:code

Source code surrounding the point of failure.

:bind

Location where local variables in ‘:vars` were extracted.

:vars

Local variables visible at the point of failure.



72
73
74
# File 'lib/detest.rb', line 72

def trace
  @trace
end

Class Method Details

.<(&block) ⇒ Object

Registers the given block to be executed before each nested test inside this test.

Examples:


D .< { puts "before each nested test" }

D .< do
  puts "before each nested test"
end


161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/detest.rb', line 161

def <(klass = nil, &block)
  if klass
    # this method is being used as a check for inheritance
    #
    # NOTE: we cannot call super() here because this module
    #       extends itself, thereby causing an infinite loop!
    #
    ancestors.include? klass
  else
    raise ArgumentError, 'block must be given' unless block
    @suite.before_each << block
  end
end

.<<(&block) ⇒ Object

Registers the given block to be executed before all nested tests inside this test.

Examples:


D .<< { puts "before all nested tests" }

D .<< do
  puts "before all nested tests"
end

Raises:

  • (ArgumentError)


204
205
206
207
# File 'lib/detest.rb', line 204

def << &block
  raise ArgumentError, 'block must be given' unless block
  @suite.before_all << block
end

.>(&block) ⇒ Object

Registers the given block to be executed after each nested test inside this test.

Examples:


D .> { puts "after each nested test" }

D .> do
  puts "after each nested test"
end

Raises:

  • (ArgumentError)


187
188
189
190
# File 'lib/detest.rb', line 187

def > &block
  raise ArgumentError, 'block must be given' unless block
  @suite.after_each << block
end

.>>(&block) ⇒ Object

Registers the given block to be executed after all nested tests inside this test.

Examples:


D .>> { puts "after all nested tests" }

D .>> do
  puts "after all nested tests"
end

Raises:

  • (ArgumentError)


221
222
223
224
# File 'lib/detest.rb', line 221

def >> &block
  raise ArgumentError, 'block must be given' unless block
  @suite.after_all << block
end

.C(symbol, message = nil, &block) ⇒ Object

Asserts that the given symbol is thrown when the given block is executed.

Examples:

no message given


C(:foo) { throw :foo, 123 } # passes, => 123
C(:foo) { throw :bar, 456 } # fails,  => 456
C(:foo) { }                 # fails,  => nil

message is given


C(:foo, ":foo must be thrown") { throw :bar, 789 } # fails, => nil

Parameters:

  • symbol (Symbol)

    Symbol that must be thrown by the given block.

  • message (defaults to: nil)

    Optional message to show in the test execution report if this assertion fails.

Returns:

  • If a value is thrown along with the expected symbol, then that value is returned.

    Otherwise, nil is returned.



494
495
496
# File 'lib/detest.rb', line 494

def C symbol, message = nil, &block
  assert_catch :assert, symbol, message, &block
end

.C!(symbol, message = nil, &block) ⇒ Object

Asserts that the given symbol is not thrown when the given block is executed.

Examples:

no message given


C!(:foo) { throw :foo, 123 } # fails,  => nil
C!(:foo) { throw :bar, 456 } # passes, => nil
C!(:foo) { }                 # passes, => nil

message is given


C!(:foo, ":foo must be thrown") { throw :bar, 789 } # passes, => nil

Parameters:

  • symbol (Symbol)

    Symbol that must not be thrown by the given block.

  • message (defaults to: nil)

    Optional message to show in the test execution report if this assertion fails.

Returns:

  • nil, always.



520
521
522
# File 'lib/detest.rb', line 520

def C! symbol, message = nil, &block
  assert_catch :negate, symbol, message, &block
end

.C?(symbol, message = nil, &block) ⇒ Boolean

Returns true if the given symbol is thrown when the given block is executed. Otherwise, returns false.

Examples:

no message given


C?(:foo) { throw :foo, 123 } # => true
C?(:foo) { throw :bar, 456 } # => false
C?(:foo) { }                 # => false

message is given


C?(:foo, ":foo must be thrown") { throw :bar, 789 } # => false

Parameters:

  • symbol (Symbol)

    Symbol that must be thrown by the given block.

  • message (defaults to: nil)

    This parameter is optional and completely ignored.

Returns:

  • (Boolean)


542
543
544
# File 'lib/detest.rb', line 542

def C? symbol, message = nil, &block
  assert_catch :sample, symbol, message, &block
end

.D(*description, &block) ⇒ Object

Defines a new test composed of the given description and the given block to execute.

This test may contain nested tests.

Tests at the outer-most level are automatically insulated from the top-level Ruby environment.

Examples:


D "a new array" do
  D .< { @array = [] }

  D "must be empty" do
    T { @array.empty? }
  end

  D "when populated" do
    D .< { @array.push 55 }

    D "must not be empty" do
      F { @array.empty? }
    end
  end
end

Parameters:

  • description (Object, Array<Object>)

    A brief title or a series of objects that describe the test being defined.



106
107
108
# File 'lib/detest.rb', line 106

def D *description, &block
  create_test @tests.empty?, *description, &block
end

.D!(*description, &block) ⇒ Object

Defines a new test that is explicitly insulated from the tests that contain it and also from the top-level Ruby environment.

This test may contain nested tests.

Examples:


D "a root-level test" do
  @outside = 1
  T { defined? @outside }
  T { @outside == 1 }

  D "an inner, non-insulated test" do
    T { defined? @outside }
    T { @outside == 1 }
  end

  D! "an inner, insulated test" do
    F { defined? @outside }
    F { @outside == 1 }

    @inside = 2
    T { defined? @inside }
    T { @inside == 2 }
  end

  F { defined? @inside }
  F { @inside == 2 }
end

Parameters:

  • description (Object, Array<Object>)

    A brief title or a series of objects that describe the test being defined.



143
144
145
# File 'lib/detest.rb', line 143

def D! *description, &block
  create_test true, *description, &block
end

.E(*kinds_then_message, &block) ⇒ Object

Asserts that one of the given kinds of exceptions is raised when the given block is executed.

Examples:

no exceptions given


E { }       # fails
E { raise } # passes

single exception given


E(ArgumentError) { raise ArgumentError }
E(ArgumentError, "argument must be invalid") { raise ArgumentError }

multiple exceptions given


E(SyntaxError, NameError) { eval "..." }
E(SyntaxError, NameError, "string must compile") { eval "..." }

Parameters:

  • kinds_then_message (...)

    Exception classes that must be raised by the given block, optionally followed by a message to show in the test execution report if this assertion fails.

    If no exception classes are given, then StandardError is assumed (similar to how a plain ‘rescue’ statement without any arguments catches StandardError).

Returns:

  • If the block raises an exception, then that exception is returned.

    Otherwise, nil is returned.



401
402
403
# File 'lib/detest.rb', line 401

def E *kinds_then_message, &block
  assert_raise :assert, *kinds_then_message, &block
end

.E!(*kinds_then_message, &block) ⇒ Object

Asserts that one of the given kinds of exceptions is not raised when the given block is executed.

Examples:

no exceptions given


E! { }       # passes
E! { raise } # fails

single exception given


E!(ArgumentError) { raise ArgumentError } # fails
E!(ArgumentError, "argument must be invalid") { raise ArgumentError }

multiple exceptions given


E!(SyntaxError, NameError) { eval "..." }
E!(SyntaxError, NameError, "string must compile") { eval "..." }

Parameters:

  • kinds_then_message (...)

    Exception classes that must be raised by the given block, optionally followed by a message to show in the test execution report if this assertion fails.

    If no exception classes are given, then StandardError is assumed (similar to how a plain ‘rescue’ statement without any arguments catches StandardError).

Returns:

  • If the block raises an exception, then that exception is returned.

    Otherwise, nil is returned.



428
429
430
# File 'lib/detest.rb', line 428

def E! *kinds_then_message, &block
  assert_raise :negate, *kinds_then_message, &block
end

.E?(*kinds_then_message, &block) ⇒ Boolean

Returns true if one of the given kinds of exceptions is raised when the given block is executed. Otherwise, returns false.

Examples:

no exceptions given


E? { }       # => false
E? { raise } # => true

single exception given


E?(ArgumentError) { raise ArgumentError } # => true

multiple exceptions given


E?(SyntaxError, NameError) { eval "..." } # => true
E!(SyntaxError, NameError, "string must compile") { eval "..." }

Parameters:

  • kinds_then_message (...)

    Exception classes that must be raised by the given block, optionally followed by a message that is completely ignored.

    If no exception classes are given, then StandardError is assumed (similar to how a plain ‘rescue’ statement without any arguments catches StandardError).

Returns:

  • (Boolean)


462
463
464
# File 'lib/detest.rb', line 462

def E? *kinds_then_message, &block
  assert_raise :sample, *kinds_then_message, &block
end

.F?(condition = nil, message = nil, &block) ⇒ Boolean

Returns true if the result of the given block is either nil or false. Otherwise, returns false.

Examples:

no message given


F? { true }  # => false
F? { false } # => true
F? { nil }   # => true

message is given


F?( "computers do not doublethink" ) { 2 + 2 == 5 } # => true

Parameters:

  • message (defaults to: nil)

    This parameter is optional and completely ignored.

Returns:

  • (Boolean)


323
324
325
# File 'lib/detest.rb', line 323

def F? condition = nil, message = nil, &block
  assert_yield :sample, false, condition, message, &block
end

.I(*messages) ⇒ Object

Adds the given messages to the test execution report beneath the currently running test.

You can think of “I” as to “inform” the user.

Examples:

single message given


I "establishing connection..."

multiple messages given


I "beginning calculation...", Math::PI, [1, 2, 3, ['a', 'b', 'c']]

Parameters:

  • messages

    Objects to be added to the test execution report.



564
565
566
# File 'lib/detest.rb', line 564

def I *messages
  @trace.concat messages
end

.I!Object

Starts an interactive debugging session at the location where this method was called.

You can think of “I!” as to “investigate” the program.



574
575
576
# File 'lib/detest.rb', line 574

def I!
  debug
end

.infoObject

Returns the details of the failure that is currently being debugged by the user.



718
719
720
# File 'lib/detest.rb', line 718

def info
  @trace.last
end

.inspectObject

Description of this release of this project.



31
32
33
# File 'lib/detest/inochi.rb', line 31

def self.inspect
  "#{PROJECT} #{VERSION} (#{RELDATE})"
end

.N(condition = nil, message = nil, &block) ⇒ Object

Asserts that the given condition or the result of the given block is nil.

Examples:

no message given


N { nil }  # passes
N { false } # fails
N { true } # fails

message is given


T("computers do not doublethink") { 2 + 2 != 5 } # passes

Parameters:

  • condition (defaults to: nil)

    The condition to be asserted. A block may be given in place of this parameter.

  • message (defaults to: nil)

    Optional message to show in the test execution report if this assertion fails.



351
352
353
# File 'lib/detest.rb', line 351

def N condition = nil, message = nil, &block
  assert_yield :assert, nil, condition, message, &block
end

.N!(condition = nil, message = nil, &block) ⇒ Object



355
356
357
# File 'lib/detest.rb', line 355

def N! condition = nil, message = nil, &block
  assert_yield :negate, nil, condition, message, &block
end

.N?(condition = nil, message = nil, &block) ⇒ Boolean

Returns:

  • (Boolean)


359
360
361
# File 'lib/detest.rb', line 359

def N? condition = nil, message = nil, &block
  assert_yield :sample, nil, condition, message, &block
end

.resetObject

Clear all test results that were recorded thus far.



709
710
711
712
# File 'lib/detest.rb', line 709

def reset
  @stats.clear
  @trace.clear
end

.S(identifier, &block) ⇒ Object

Mechanism for sharing code between tests.

If a block is given, it is shared under the given identifier. Otherwise, the code block that was previously shared under the given identifier is injected into the closest insulated Detest test that contains the call to this method.

Examples:


S :knowledge do
  #...
end

D "some test" do
  S :knowledge
end

D "another test" do
  S :knowledge
end

Parameters:

  • identifier (Symbol, Object)

    An object that identifies shared code. This must be common knowledge to all parties that want to partake in the sharing.



607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
# File 'lib/detest.rb', line 607

def S identifier, &block
  if block_given?
    if already_shared = @share[identifier]
      raise ArgumentError, "A code block #{already_shared.inspect} has "\
        "already been shared under the identifier #{identifier.inspect}."
    end

    @share[identifier] = block

  elsif block = @share[identifier]
    if @tests.empty?
      raise "Cannot inject code block #{block.inspect} shared under "\
        "identifier #{identifier.inspect} outside of a Detest test."
    else
      # find the closest insulated parent test; this should always
      # succeed because root-level tests are insulated by default
      test = @tests.reverse.find {|t| t.sandbox } or raise IndexError
      test.sandbox.instance_eval(&block)
    end

  else
    raise ArgumentError, "No code block is shared under identifier "\
      "#{identifier.inspect}."
  end
end

.S!(identifier, &block) ⇒ Object

Shares the given code block under the given identifier and then immediately injects that code block into the closest insulated Detest test that contains the call to this method.

Examples:


D "some test" do
  S! :knowledge do
    #...
  end
end

D "another test" do
  S :knowledge
end

Parameters:

  • identifier (Symbol, Object)

    An object that identifies shared code. This must be common knowledge to all parties that want to partake in the sharing.



653
654
655
656
657
# File 'lib/detest.rb', line 653

def S! identifier, &block
  raise 'block must be given' unless block_given?
  S identifier, &block
  S identifier
end

.S?(identifier) ⇒ Boolean

Checks whether any code has been shared under the given identifier.

Returns:

  • (Boolean)


662
663
664
# File 'lib/detest.rb', line 662

def S? identifier
  @share.key? identifier
end

.startObject

Executes all tests defined thus far and stores the results in trace and stats.



670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
# File 'lib/detest.rb', line 670

def start
  # execute the tests
  start_time = Time.now
  catch :DIFECTS_STOP do
    BINDINGS.track do
      execute
    end
  end
  @stats[:time] = Time.now - start_time

  # print test results
  unless @stats.key? :fail or @stats.key? :error
    #
    # show execution trace only if all tests passed.
    # otherwise, we will be repeating already printed
    # failure details and obstructing the developer!
    #
    display @trace
  end

  display @stats

ensure
  @tests.clear
  @share.clear
  @files.clear
end

.stopObject

Stops the execution of the start method or raises an exception if that method is not currently executing.



702
703
704
# File 'lib/detest.rb', line 702

def stop
  throw :DIFECTS_STOP
end

.T(condition = nil, message = nil, &block) ⇒ Object Also known as: F!

Asserts that the given condition or the result of the given block is neither nil nor false and returns that result.

Examples:

no message given


T { true }  # passes
T { false } # fails
T { nil }   # fails

message is given


T("computers do not doublethink") { 2 + 2 != 5 } # passes

Parameters:

  • condition (defaults to: nil)

    The condition to be asserted. A block may be given in place of this parameter.

  • message (defaults to: nil)

    Optional message to show in the test execution report if this assertion fails.



251
252
253
# File 'lib/detest.rb', line 251

def T condition = nil, message = nil, &block
  assert_yield :assert, true, condition, message, &block
end

.T!(condition = nil, message = nil, &block) ⇒ Object Also known as: F

Asserts that the given condition or the result of the given block is either nil or false and returns that result.

Examples:

no message given


T! { true }  # fails
T! { false } # passes
T! { nil }   # passes

message is given


T!("computers do not doublethink") { 2 + 2 == 5 } # passes

Parameters:

  • condition (defaults to: nil)

    The condition to be asserted. A block may be given in place of this parameter.

  • message (defaults to: nil)

    Optional message to show in the test execution report if this assertion fails.



274
275
276
# File 'lib/detest.rb', line 274

def T! condition = nil, message = nil, &block
  assert_yield :negate, true, condition, message, &block
end

.T?(condition = nil, message = nil, &block) ⇒ Boolean

Returns true if the given condition or the result of the given block is neither nil nor false. Otherwise, returns false.

Examples:

no message given


T? { true }  # => true
T? { false } # => false
T? { nil }   # => false

message is given


T?("computers do not doublethink") { 2 + 2 != 5 } # => true

Parameters:

  • message (defaults to: nil)

    This parameter is optional and completely ignored.

  • condition (defaults to: nil)

    The condition to be asserted. A block may be given in place of this parameter.

Returns:

  • (Boolean)


299
300
301
# File 'lib/detest.rb', line 299

def T? condition = nil, message = nil, &block
  assert_yield :sample, true, condition, message, &block
end

Instance Method Details

#after(what, &block) ⇒ Object



21
22
23
24
25
26
27
28
29
30
# File 'lib/detest/spec.rb', line 21

def after what, &block
  meth =
    case what
    when :each then :>
    when :all  then :>>
    else raise ArgumentError, what
    end

  send meth, &block
end

#before(what, &block) ⇒ Object



10
11
12
13
14
15
16
17
18
19
# File 'lib/detest/spec.rb', line 10

def before what, &block
  meth =
    case what
    when :each then :<
    when :all  then :<<
    else raise ArgumentError, what
    end

  send meth, &block
end