Module: Dfect

Included in:
Object
Defined in:
lib/dfect.rb,
lib/dfect/mini.rb,
lib/dfect/spec.rb,
lib/dfect/unit.rb,
lib/dfect/inochi.rb

Constant Summary

D =

Allows 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
PROJECT =

Official name of this project.

"Dfect"
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/dfect/"
VERSION =

Number of this release of this project.

"2.1.0"
RELDATE =

Date of this release of this project.

"2010-03-31"
INSTDIR =

Location of this release of this project.

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

RubyGems required by this project during runtime.

Examples:

RUNTIME = {
  # 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" => [],
}
{}
DEVTIME =

RubyGems required by this project during development.

Examples:

DEVTIME = {
  # 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" => [],
}
{
  "inochi" => [ "~> 2" ], # for managing this project
}

Class Attribute Summary

Class Method Summary

Instance Method Summary

Class Attribute Details

+ (Object) options

Hash of choices that affect how Dfect operates.

:debug
Launch an interactive debugger during assertion failures so the user can investigate them.

The default value is $DEBUG.

:quiet
Do not print the report after executing all tests.

The default value is false.



88
89
90
# File 'lib/dfect.rb', line 88

def options
  @options
end

+ (Object) report (readonly)

Hash of test results, assembled by Dfect.run.

:trace
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.
:code
Source code surrounding the point of failure.
:vars
Local variables visible at the point of failure.
:call
Stack trace leading to the point of failure.
:stats
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.


70
71
72
# File 'lib/dfect.rb', line 70

def report
  @report
end

Class Method Details

+ (Object) <(&block)

Examples:

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

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

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



177
178
179
180
181
182
183
184
185
# File 'lib/dfect.rb', line 177

def <(*args, &block)
  if args.empty?
    raise ArgumentError, 'block must be given' unless block
    @suite.before_each << block
  else
    # the < method is being used as a check for inheritance
    super
  end
end

+ (Object) <<(&block)

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)


216
217
218
219
# File 'lib/dfect.rb', line 216

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

+ (Object) >(&block)

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)


199
200
201
202
# File 'lib/dfect.rb', line 199

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

+ (Object) >>(&block)

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)


233
234
235
236
# File 'lib/dfect.rb', line 233

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

+ (Object) C(symbol, message = nil, &block)

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 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.



469
470
471
# File 'lib/dfect.rb', line 469

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

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

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 report if this assertion fails.

Returns:

  • nil, always.



495
496
497
# File 'lib/dfect.rb', line 495

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

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

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)


517
518
519
# File 'lib/dfect.rb', line 517

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

+ (Object) D(*description, &block)

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:

  • (Object, Array<Object>) description

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



122
123
124
# File 'lib/dfect.rb', line 122

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

+ (Object) D!(*description, &block)

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:

  • (Object, Array<Object>) description

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



159
160
161
# File 'lib/dfect.rb', line 159

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

+ (Object) E(*kinds_then_message, &block)

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 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.



376
377
378
# File 'lib/dfect.rb', line 376

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

+ (Object) E!(*kinds_then_message, &block)

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 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.



403
404
405
# File 'lib/dfect.rb', line 403

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

+ (Boolean) E?(*kinds_then_message, &block)

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)


437
438
439
# File 'lib/dfect.rb', line 437

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

+ (Boolean) F?(message = nil, &block)

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)


335
336
337
# File 'lib/dfect.rb', line 335

def F? message = nil, &block
  not T? message, &block
end

+ (Object) info

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



675
676
677
# File 'lib/dfect.rb', line 675

def info
  @trace.last
end

+ (Object) inspect

Description of this release of this project.



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

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

+ (Object) L(*messages)

Adds the given messages to the report inside the section of the currently running test.

You can think of “L” as “to log something”.

Examples:

single message given

L "establishing connection..."

multiple messages given

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

Parameters:

  • messages

    Objects to be added to the report.



539
540
541
# File 'lib/dfect.rb', line 539

def L *messages
  @trace.concat messages
end

+ (Object) require(gem_name_or_library)

Loads the correct version (as defined by the RUNTIME or DEVTIME constant in this module) of the given gem or the gem that contains the given library.



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/dfect/inochi.rb', line 85

def self.require gem_name_or_library
  # prepare the correct version of the gem for loading
  if respond_to? :gem
    gem_name = gem_name_or_library.to_s.sub(%r{/.*$}, '')
    if gem_version = RUNTIME[gem_name] || DEVTIME[gem_name]
      begin
        gem gem_name, *gem_version
      rescue LoadError => error
        warn "#{self.inspect}: #{error}"
      end
    end
  end

  # do the loading
  super
end

+ (Object) run(continue = true)

Executes all tests defined thus far and stores the results in Dfect.report.

Parameters:

  • (Boolean) continue (defaults to: true)

    If true, results from previous executions will not be cleared.



636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
# File 'lib/dfect.rb', line 636

def run continue = true
  # clear previous results
  unless continue
    @stats.clear
    @trace.clear
    @tests.clear
  end

  # make new results
  start = Time.now
  catch(:stop_dfect_execution) { execute }
  finish = Time.now
  @stats[:time] = finish - start

  # print new 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
end

+ (Object) S(identifier, &block)

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 Dfect 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:

  • (Symbol, Object) identifier

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



572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
# File 'lib/dfect.rb', line 572

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 Dfect 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 }
      test.sandbox.instance_eval(&block)
    end

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

+ (Object) S!(identifier, &block)

Shares the given code block under the given identifier and then immediately injects that code block into the closest insulated Dfect 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:

  • (Symbol, Object) identifier

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



615
616
617
618
619
# File 'lib/dfect.rb', line 615

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

+ (Boolean) S?(identifier)

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

Returns:

  • (Boolean)


624
625
626
# File 'lib/dfect.rb', line 624

def S? identifier
  @share.key? identifier
end

+ (Object) stop

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



667
668
669
# File 'lib/dfect.rb', line 667

def stop
  throw :stop_dfect_execution
end

+ (Object) T(condition = nil, message = nil, &block) 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 report if this assertion fails.



263
264
265
# File 'lib/dfect.rb', line 263

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

+ (Object) T!(condition = nil, message = nil, &block) 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 report if this assertion fails.



286
287
288
# File 'lib/dfect.rb', line 286

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

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

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)


311
312
313
# File 'lib/dfect.rb', line 311

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

Instance Method Details

- (Object) after(what, &block)



21
22
23
24
25
26
27
28
29
30
# File 'lib/dfect/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

- (Object) before(what, &block)



10
11
12
13
14
15
16
17
18
19
# File 'lib/dfect/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