Class: BareTest::Assertion

Inherits:
Object
  • Object
show all
Defined in:
lib/baretest/assertion.rb,
lib/baretest/assertion/skip.rb,
lib/baretest/assertion/context.rb,
lib/baretest/assertion/failure.rb,
lib/baretest/assertion/support.rb

Overview

Defines an assertion An assertion belongs to a suite and consists of a description and a block. To verify the assertion, the suite’s (and its ancestors) setup blocks are executed, then the assertions block is executed and after that, the suite’s (and ancestors) teardown blocks are invoked.

An assertion has 5 possible states, see Assertion#status for a list of them.

There are various helper methods in BareTest::Assertion::Support which help you defining nicer diagnostics or just easier ways to test common scenarios. The following are test helpers:

  • Kernel#raises(exception_class=StandardError)

  • Kernel#within_delta(a, b, delta)

  • Kernel#equal_unordered(a,b)

  • Enumerable#equal_unordered(other)

Defined Under Namespace

Modules: Support Classes: Context, Failure, Skip

Constant Summary collapse

PassthroughExceptions =

The exceptions baretest will not rescue (NoMemoryError, SignalException, Interrupt and SystemExit)

[NoMemoryError, SignalException, Interrupt, SystemExit]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(suite, description, opt = nil, &block) ⇒ Assertion

suite

The suite the Assertion belongs to

description

A descriptive string about what this Assertion tests.

&block

The block definition. Without one, the Assertion will have a :pending status.



63
64
65
66
67
68
69
70
71
72
# File 'lib/baretest/assertion.rb', line 63

def initialize(suite, description, opt=nil, &block)
  @suite       = suite
  @description = (description || "No description given")
  @block       = block
  @skipped     = false
  if opt then
    skip_reason = opt[:skip]
    skip(skip_reason == true ? "Tagged as skipped" : skip_reason) if skip_reason
  end
end

Instance Attribute Details

#blockObject (readonly)

The block specifying the assertion



45
46
47
# File 'lib/baretest/assertion.rb', line 45

def block
  @block
end

#codeObject

The file this assertion is specified in. Not contructed by Assertion itself.



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

def code
  @code
end

#descriptionObject (readonly)

The description of this assertion.



39
40
41
# File 'lib/baretest/assertion.rb', line 39

def description
  @description
end

#fileObject

The file this assertion is specified in. Not contructed by Assertion itself.



51
52
53
# File 'lib/baretest/assertion.rb', line 51

def file
  @file
end

#lineObject

The line this assertion is specified on. Not contructed by Assertion itself.



54
55
56
# File 'lib/baretest/assertion.rb', line 54

def line
  @line
end

#linesObject

The lines this assertion spans. Not contructed by Assertion itself.



57
58
59
# File 'lib/baretest/assertion.rb', line 57

def lines
  @lines
end

#suiteObject (readonly)

The suite this assertion belongs to



42
43
44
# File 'lib/baretest/assertion.rb', line 42

def suite
  @suite
end

Instance Method Details

#execute(with_setup = nil, and_teardown = nil) ⇒ Object

Returns a Status Executes with_setup, then the assertions defining block, and in the end and_teardown. Usually with_setup and and_teardown are supplied by the containing suite.



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/baretest/assertion.rb', line 112

def execute(with_setup=nil, and_teardown=nil)
  if @skipped then
    status = Status.new(self, :manually_skipped, nil, @skipped)
  elsif !@block
    status = Status.new(self, :pending)
  else
    handlers        = @suite ? @suite.ancestors.inject({}) { |handlers, suite| handlers.merge(suite.verification_exception_handlers) } : nil
    context         = ::BareTest::Assertion::Context.new(self)
    status          = execute_phase(:setup, context, with_setup.map { |s| [[s.value], s.block] }) if with_setup
    setup_failed    = status
    status          = execute_phase(:exercise, context, @block, handlers) unless status
    teardown_status = execute_phase(:teardown, context, and_teardown.map { |t| [nil, t] }) unless (setup_failed || !and_teardown)
    status = status || teardown_status || Status.new(self, :success, context)
  end

  status
end

#execute_phase(name, context, code, handlers = nil) ⇒ Object

A phase can result in either success, skip, failure or error Execute_phase will simply return nil upon success, all other cases will generate a full Status instance. This is for practical reasons - it means you can go through several phases, looking for the first non-nil one.



135
136
137
138
139
140
141
142
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
# File 'lib/baretest/assertion.rb', line 135

def execute_phase(name, context, code, handlers=nil)
  status         = nil
  skip_reason    = nil
  failure_reason = nil
  exception      = nil

  begin
    if code.is_a?(Array) then
      code.each do |args, block| context.instance_exec(*args, &block) end
    else
      unless context.instance_eval(&code)
        failure_reason = "Assertion failed" 
        status         = :failure
      end
    end
  rescue *PassthroughExceptions
    raise # passthrough-exceptions must be passed through
  rescue ::BareTest::Assertion::Failure => failure
    status         = :failure
    failure_reason = failure.message
  rescue ::BareTest::Assertion::Skip => skip
    status         = :manually_skipped
    skip_reason    = skip.message
  rescue Exception => exception
    exception_class = exception.class
    handled_by      = handlers && handlers.find { |handling, handler| exception_class <= handling }
    if handled_by then
      handler       = handled_by.last
      return handler.call(self, context, exception) # FIXME: ugly mid-method return - work around it returning a complete Status
    end
    status         = :error
    failure_reason = "An error occurred during #{name}: #{exception}"
    exception      = exception
  end

  status && Status.new(self, status, context, skip_reason, failure_reason, exception)
end

#idObject

An ID, usable for persistence



75
76
77
78
79
80
# File 'lib/baretest/assertion.rb', line 75

def id
  @id ||= [
    @description,
    *(@suite && @suite.ancestors.map { |suite| suite.description })
  ].compact.join("\f")
end

#inspectObject

:nodoc:



177
178
179
# File 'lib/baretest/assertion.rb', line 177

def inspect # :nodoc:
  sprintf "#<%s:%08x suite=%p %p>", self.class, object_id>>1, @suite, @description
end

#interpolated_description(substitutes) ⇒ Object

The description allows substitutes in the form “:identifier” and “:identifier” (the latter in case of ajanced characters that don’t belong to the identifier, like “:identifierstuff”). This method will interploate those substitutes. This is relevant with regards to setup variants.



98
99
100
101
102
103
104
105
106
# File 'lib/baretest/assertion.rb', line 98

def interpolated_description(substitutes)
  if substitutes.empty? then
    @description
  else
    @description.gsub(/:(?:#{substitutes.keys.join('|')})\b/) { |m|
      substitutes[m[1..-1].to_sym]
    }
  end
end

#skip(reason = nil) ⇒ Object

Marks this assertion as manually skipped.



88
89
90
91
# File 'lib/baretest/assertion.rb', line 88

def skip(reason=nil)
  @skipped ||= []
  @skipped  |= reason ? Array(reason) : ['Manually skipped']
end

#skipped?Boolean

Returns whether this assertion has been marked as manually skipped.

Returns:

  • (Boolean)


83
84
85
# File 'lib/baretest/assertion.rb', line 83

def skipped?
  !!@skipped
end

#to_sObject

:nodoc:



173
174
175
# File 'lib/baretest/assertion.rb', line 173

def to_s # :nodoc:
  sprintf "%s %s", self.class, @description
end