Class: Spectre::RunContext

Inherits:
Object show all
Includes:
Delegate
Defined in:
lib/spectre.rb

Constant Summary collapse

DEFAULT_ASYNC_NAME =

The default identifier of async blocks.

'default'
@@current =
nil
@@skip_count =
0

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Delegate

#instance_eval, #instance_exec, #method_missing, #respond_to_missing?

Constructor Details

#initialize(engine, parent, type, bag = nil) ⇒ RunContext

Returns a new instance of RunContext.



815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
# File 'lib/spectre.rb', line 815

def initialize engine, parent, type, bag = nil
  @engine = engine
  @parent = parent
  @type = type
  @logs = []

  @threads = {}

  @name = parent.name

  # If the run type is an actual spec, the context
  # of the run is its parent +Specification+ 's parent.
  if type == :spec
    @context = parent.parent
  else
    # Otherwise the run context is the parent itself
    # This is the case when a setup or teardown block
    # is executet, which uses its own run context.
    @context = parent
    @name += "-#{type}"
  end

  @bag = OpenStruct.new(bag)

  @properties = {}

  @evaluations = []
  @error = nil
  @skipped = false

  @started = Time.now

  begin
    @@current = self
    yield self
  ensure
    @finished = Time.now
    @@current = nil
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Spectre::Delegate

Instance Attribute Details

#bagObject (readonly)

A variable bag to store values across setup, teardown, before, after and it blocks.

setup do
  bag.foo = 'bar'
end

it 'does something' do
  assert bag.foo.to be 'bar'
end


793
794
795
# File 'lib/spectre.rb', line 793

def bag
  @bag
end

#contextObject (readonly)

:stopdoc:



797
798
799
# File 'lib/spectre.rb', line 797

def context
  @context
end

#dataObject (readonly)

:stopdoc:



797
798
799
# File 'lib/spectre.rb', line 797

def data
  @data
end

#errorObject (readonly)

:stopdoc:



797
798
799
# File 'lib/spectre.rb', line 797

def error
  @error
end

#evaluationsObject (readonly)

:stopdoc:



797
798
799
# File 'lib/spectre.rb', line 797

def evaluations
  @evaluations
end

#finishedObject (readonly)

:stopdoc:



797
798
799
# File 'lib/spectre.rb', line 797

def finished
  @finished
end

#logsObject (readonly)

:stopdoc:



797
798
799
# File 'lib/spectre.rb', line 797

def logs
  @logs
end

#nameObject (readonly)

:stopdoc:



797
798
799
# File 'lib/spectre.rb', line 797

def name
  @name
end

#parentObject (readonly)

:stopdoc:



797
798
799
# File 'lib/spectre.rb', line 797

def parent
  @parent
end

#propertiesObject (readonly)

:stopdoc:



797
798
799
# File 'lib/spectre.rb', line 797

def properties
  @properties
end

#startedObject (readonly)

:stopdoc:



797
798
799
# File 'lib/spectre.rb', line 797

def started
  @started
end

#typeObject (readonly)

:stopdoc:



797
798
799
# File 'lib/spectre.rb', line 797

def type
  @type
end

Class Method Details

.currentObject

The current executing RunContext



811
812
813
# File 'lib/spectre.rb', line 811

def self.current
  @@current
end

Instance Method Details

#async(name = DEFAULT_ASYNC_NAME) ⇒ Object

Executes the given block asyncronously in a new thread. This thread can be awaited with await and the given name. You can start multiple threads with the same name. Using this name with await will wait for all threads to finish.

The last line in the async block will be returned as a result. and is available when await ing the threads.

async do
  info 'do some async stuff'

  'a result'
end

result = await


1059
1060
1061
1062
# File 'lib/spectre.rb', line 1059

def async(name = DEFAULT_ASYNC_NAME, &)
  @threads[name] ||= []
  @threads[name] << Thread.new(&)
end

#await(name = DEFAULT_ASYNC_NAME) ⇒ Object

returns

An Array of previously executed async blocks

with the same name.

Waits for the threads created with async to finish. Awaits all threads with the given name.



1071
1072
1073
1074
1075
1076
# File 'lib/spectre.rb', line 1071

def await name = DEFAULT_ASYNC_NAME
  return unless @threads.key? name

  threads = @threads.delete(name)
  threads.map(&:join)
end

#durationObject

returns

The duration of the previously executed measure block.

The duration optained by measure.



1038
1039
1040
# File 'lib/spectre.rb', line 1038

def duration
  @measured_duration
end

#execute(data) ⇒ Object

Executes the given block in the context of this RunContext. For internal use only. Do not execute within an it block.



860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
# File 'lib/spectre.rb', line 860

def execute(data, &)
  @data = data
  instance_exec(data.is_a?(Hash) ? OpenStruct.new(data) : data, &)
rescue AbortException
  # Do nothing. The run will be ended here
rescue Interrupt
  @skipped = true
  @engine.formatter.log(:debug, nil, :skipped, 'canceled by user')
  @engine.logger.info("#{@parent.desc} - canceled by user")
  raise Interrupt if (@@skip_count += 1) > 2
rescue StandardError => e
  @error = e
  @engine.formatter.log(:fatal, e.message, :error, e.class.name)
  @engine.logger.fatal("#{e.message}\n#{e.backtrace.join("\n")}")
end

#fail_with(message) ⇒ Object

deprecated

use report instead.

Raise a failure error. Using this method within an assert or expect block will report an error and aborts the run immediately.

assert 'something' do
  fail_with 'a detailed message'
end

Raises:



942
943
944
# File 'lib/spectre.rb', line 942

def fail_with message
  raise Failure, message
end

#group(desc) ⇒ Object

Groups code in a block. This is solely used for structuring tests and will not have effect on test reports.



1083
1084
1085
1086
1087
1088
# File 'lib/spectre.rb', line 1083

def group(desc, &)
  @engine.logger.correlate do
    @engine.logger.debug("group \"#{desc}\"")
    @engine.formatter.scope(desc, :group, &)
  end
end

#measureObject

Takes a block and measures the time of the execution. The duration in seconds is available through duration.

measure do
  info 'do some long running stuff'
  sleep 1
end

info "run duration was #{duration}"


1024
1025
1026
1027
1028
1029
1030
# File 'lib/spectre.rb', line 1024

def measure
  start_time = Time.now
  yield
  end_time = Time.now

  @measured_duration = end_time - start_time
end

#methodObject

:method: expect :args: desc, &

Expect a specific condition. If a block is given it creates an EvaluationContext and its methods are available. If a failure is reported within this block, the run will continue.

foo = 'bar'

expect foo.to be 'bar'

expect 'a certain condition to be true' do
  report 'it was not' if foo == 'bar'
end


911
912
913
914
915
916
917
# File 'lib/spectre.rb', line 911

%i[debug info warn].each do |method|
  define_method(method) do |message|
    message = message.to_s
    @engine.logger.send(method, message)
    @engine.formatter.log(method, message)
  end
end

#observe(desc) ⇒ Object

Observes the given block and catches all errors within this block. If errors or failures occur the test will not fail or aborted.

The result of the observation can be retreived through success?

observe do
  info 'do some stuff'
end

assert success?.to be true

observe do
  raise StandardError, 'Oops!'
end

assert success?.to be false


1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
# File 'lib/spectre.rb', line 1110

def observe desc
  @engine.formatter.log(:info, "observe #{desc}") do
    yield
    @success = true
    [:info, :ok, nil]
  rescue StandardError => e
    @success = false
    @engine.logger.warn("#{e.message}\n#{e.backtrace.join("\n")}")
    [:info, :warn, e.message]
  end
end

#property(**kwargs) ⇒ Object

Adds the given key-value arguments to the run report. Use this to add generated values during the run to the test report.

property key: 'value'


1009
1010
1011
# File 'lib/spectre.rb', line 1009

def property **kwargs
  @properties.merge!(kwargs)
end

#resourcesObject

Access the loaded resources (files). The path parameter is relative to the resource directory.

resources['path/to/some.txt']


927
928
929
# File 'lib/spectre.rb', line 927

def resources
  @engine.resources
end

#run(desc, with: nil) ⇒ Object Also known as: also

Run the mixin with the given name and parameters

run 'additional actions' do
  with some_param: 42,
       another_param: 'foo'
end


1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
# File 'lib/spectre.rb', line 1137

def run(desc, with: nil, &)
  @engine.formatter.scope(desc, :mixin) do
    raise "mixin \"#{desc}\" not found" unless @engine.mixins.key? desc

    mixin = @engine.mixins[desc]
    mixin.instance_eval(&) if block_given?

    @engine.logger.correlate do
      @engine.logger.debug("execute mixin \"#{desc}\"")
      result = mixin.run(self, with)
      return result.is_a?(Hash) ? OpenStruct.new(result) : result
    end
  end
end

#skip(message) ⇒ Object

Skip the run for this spec. This can be used to skip spec runs when a certain condition occurs.

skip 'subject is not yet ready to be tests' unless service_is_ready()

Raises:



1163
1164
1165
1166
1167
# File 'lib/spectre.rb', line 1163

def skip message
  @skipped = true
  @engine.logger.info("#{message} - canceled by user")
  raise AbortException
end

#statusObject

The status of the current run. One of :error, :failed, :skipped or :success



880
881
882
883
884
885
886
# File 'lib/spectre.rb', line 880

def status
  return :error if @error
  return :failed if @evaluations.any? { |x| x.failures.any? }
  return :skipped if @skipped

  :success
end

#success?Boolean

Returns the status of the pervious observe block

Returns:

  • (Boolean)


1125
1126
1127
# File 'lib/spectre.rb', line 1125

def success?
  @success.nil? || @success
end