Class: ContextState

Inherits:
Object show all
Defined in:
lib/mspec/runner/context.rb

Overview

Holds the state of the describe block that is being evaluated. Every example (i.e. it block) is evaluated in a context, which may include state set up in before :each or before :all blocks.

– A note on naming: this is named ContextState rather than DescribeState because describe is the keyword in the DSL for refering to the context in which an example is evaluated, just as it refers to the example itself. ++

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(mod, options = nil) ⇒ ContextState

Returns a new instance of ContextState.



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/mspec/runner/context.rb', line 15

def initialize(mod, options=nil)
  @to_s = mod.to_s
  if options.is_a? Hash
    @options = options
  else
    @to_s += "#{".:#".include?(options[0,1]) ? "" : " "}#{options}" if options
    @options = { }
  end
  @options[:shared] ||= false

  @parsed   = false
  @before   = { :all => [], :each => [] }
  @after    = { :all => [], :each => [] }
  @pre      = {}
  @post     = {}
  @examples = []
  @parent   = nil
  @parents  = [self]
  @children = []

  @mock_verify         = Proc.new { Mock.verify_count }
  @mock_cleanup        = Proc.new { Mock.cleanup }
  @expectation_missing = Proc.new { raise SpecExpectationNotFoundError }
end

Instance Attribute Details

#childrenObject (readonly)

Returns the value of attribute children.



13
14
15
# File 'lib/mspec/runner/context.rb', line 13

def children
  @children
end

#examplesObject (readonly)

Returns the value of attribute examples.



13
14
15
# File 'lib/mspec/runner/context.rb', line 13

def examples
  @examples
end

#parentObject

Returns the value of attribute parent.



13
14
15
# File 'lib/mspec/runner/context.rb', line 13

def parent
  @parent
end

#parentsObject (readonly)

Returns the value of attribute parents.



13
14
15
# File 'lib/mspec/runner/context.rb', line 13

def parents
  @parents
end

#stateObject (readonly)

Returns the value of attribute state.



13
14
15
# File 'lib/mspec/runner/context.rb', line 13

def state
  @state
end

#to_sObject (readonly)

Returns the value of attribute to_s.



13
14
15
# File 'lib/mspec/runner/context.rb', line 13

def to_s
  @to_s
end

Instance Method Details

#adopt(parent) ⇒ Object

Adds a nested ContextState in a shared ContextState to a containing ContextState.

Normal adoption is from the parent’s perspective. But adopt is a good verb and it’s reasonable for the child to adopt the parent as well. In this case, manipulating state from inside the child avoids needlessly exposing the state to manipulate it externally in the dup. (See #it_should_behave_like)



86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/mspec/runner/context.rb', line 86

def adopt(parent)
  self.parent = parent

  @examples = @examples.map do |example|
    example = example.dup
    example.context = self
    example
  end

  children = @children
  @children = []

  children.each { |child| child.dup.adopt self }
end

#after(what, &block) ⇒ Object

Records after(:each) and after(:all) blocks.



122
123
124
125
# File 'lib/mspec/runner/context.rb', line 122

def after(what, &block)
  return if MSpec.guarded?
  block ? @after[what].unshift(block) : @after[what]
end

#before(what, &block) ⇒ Object

Records before(:each) and before(:all) blocks.



116
117
118
119
# File 'lib/mspec/runner/context.rb', line 116

def before(what, &block)
  return if MSpec.guarded?
  block ? @before[what].push(block) : @before[what]
end

#child(child) ⇒ Object

Add the ContextState instance child to the list of nested describe blocks.



74
75
76
# File 'lib/mspec/runner/context.rb', line 74

def child(child)
  @children << child
end

#describe(&block) ⇒ Object

Evaluates the block and resets the toplevel ContextState to #parent.



137
138
139
140
141
# File 'lib/mspec/runner/context.rb', line 137

def describe(&block)
  @parsed = protect @to_s, block, false
  MSpec.register_current parent
  MSpec.register_shared self if shared?
end

#descriptionObject

Returns a description string generated from self and all parents



144
145
146
# File 'lib/mspec/runner/context.rb', line 144

def description
  @description ||= parents.map { |p| p.to_s }.compact.join(" ")
end

#filter_examplesObject

Removes filtered examples. Returns true if there are examples left to evaluate.



184
185
186
187
# File 'lib/mspec/runner/context.rb', line 184

def filter_examples
  @examples.reject! { |ex| ex.filtered? }
  not @examples.empty?
end

#initialize_copy(other) ⇒ Object

Remove caching when a ContextState is dup’d for shared specs.



41
42
43
44
# File 'lib/mspec/runner/context.rb', line 41

def initialize_copy(other)
  @pre  = {}
  @post = {}
end

#it(desc, &block) ⇒ Object

Creates an ExampleState instance for the block and stores it in a list of examples to evaluate unless the example is filtered.



129
130
131
132
133
134
# File 'lib/mspec/runner/context.rb', line 129

def it(desc, &block)
  example = ExampleState.new(self, desc, block)
  MSpec.actions :add, example
  return if MSpec.guarded?
  @examples << example
end

#it_should_behave_like(desc) ⇒ Object

Injects the before/after blocks and examples from the shared describe block into this ContextState instance.



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/mspec/runner/context.rb', line 150

def it_should_behave_like(desc)
  return if MSpec.guarded?

  unless state = MSpec.retrieve_shared(desc)
    raise Exception, "Unable to find shared 'describe' for #{desc}"
  end

  state.before(:all).each { |b| before :all, &b }
  state.before(:each).each { |b| before :each, &b }
  state.after(:each).each { |b| after :each, &b }
  state.after(:all).each { |b| after :all, &b }

  state.examples.each do |example|
    example = example.dup
    example.context = self
    @examples << example
  end

  state.children.each do |child|
    child.dup.adopt self
  end
end

#post(what) ⇒ Object

Returns a list of all after(what) blocks from self and any parents. The list is in reverse order. In other words, the blocks defined in inner describes are in the list before those defined in outer describes, and in a particular describe block those defined later are in the list before those defined earlier.



111
112
113
# File 'lib/mspec/runner/context.rb', line 111

def post(what)
  @post[what] ||= parents.inject([]) { |l, s| l.unshift(*s.after(what)) }
end

#pre(what) ⇒ Object

Returns a list of all before(what) blocks from self and any parents.



102
103
104
# File 'lib/mspec/runner/context.rb', line 102

def pre(what)
  @pre[what] ||= parents.inject([]) { |l, s| l.push(*s.before(what)) }
end

#processObject

Evaluates the examples in a ContextState. Invokes the MSpec events for :enter, :before, :after, :leave.



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
# File 'lib/mspec/runner/context.rb', line 191

def process
  MSpec.register_current self

  if @parsed and filter_examples
    MSpec.shuffle @examples if MSpec.randomize?
    MSpec.actions :enter, description

    if protect "before :all", pre(:all)
      @examples.each do |state|
        MSpec.repeat do
          @state  = state
          example = state.example
          MSpec.actions :before, state

          if protect "before :each", pre(:each)
            MSpec.clear_expectations
            if example
              passed = protect nil, example
              MSpec.actions :example, state, example
              protect nil, @expectation_missing unless MSpec.expectation? or not passed
            end
            protect "after :each", post(:each)
            protect "Mock.verify_count", @mock_verify
          end

          protect "Mock.cleanup", @mock_cleanup
          MSpec.actions :after, state
          @state = nil
        end
      end
      protect "after :all", post(:all)
    else
      protect "Mock.cleanup", @mock_cleanup
    end

    MSpec.actions :leave
  end

  MSpec.register_current nil
  children.each { |child| child.process }
end

#protect(what, blocks, check = true) ⇒ Object

Evaluates each block in blocks using the MSpec.protect method so that exceptions are handled and tallied. Returns true and does NOT evaluate any blocks if check is true and MSpec.mode?(:pretend) is true.



177
178
179
180
# File 'lib/mspec/runner/context.rb', line 177

def protect(what, blocks, check=true)
  return true if check and MSpec.mode? :pretend
  Array(blocks).all? { |block| MSpec.protect what, &block }
end

#shared?Boolean

Returns true if this is a shared ContextState. Essentially, when created with: describe “Something”, :shared => true { … }

Returns:

  • (Boolean)


48
49
50
# File 'lib/mspec/runner/context.rb', line 48

def shared?
  return @options[:shared]
end