module Micronaut class Behaviour include Micronaut::Matchers attr_accessor :running_example def self.inherited(klass) super Micronaut.world.behaviours << klass end def self.extended_modules #:nodoc: ancestors = class << self; ancestors end ancestors.select { |mod| mod.class == Module } - [ Object, Kernel ] end def self.befores @_befores ||= { :all => [], :each => [] } end def self.before_eachs befores[:each] end def self.before_alls befores[:all] end def self.before(type=:each, options={}, &block) befores[type] << [options, block] end def self.afters @_afters ||= { :all => [], :each => [] } end def self.after_eachs afters[:each] end def self.after_alls afters[:all] end def self.after(type=:each, options={}, &block) afters[type] << [options, block] end def self.example(desc=nil, options={}, &block) examples << Micronaut::Example.new(self, desc, options.update(:caller => caller[0]), block) end def self.alias_example_to(new_alias, extra_options={}) new_alias = <<-END_RUBY def self.#{new_alias}(desc=nil, options={}, &block) updated_options = options.update(:caller => caller[0]) updated_options.update(#{extra_options.inspect}) examples << Micronaut::Example.new(self, desc, updated_options, block) end END_RUBY module_eval(new_alias, __FILE__, __LINE__) end alias_example_to :it alias_example_to :focused, :focused => true def self.examples @_examples ||= [] end def self.examples_to_run @_examples_to_run ||= [] end # This method is friggin' gigantic, and must be stopped # def self.set_it_up(*args) @metadata = {} extra_metadata = args.last.is_a?(Hash) ? args.pop : {} if args.first.is_a?(String) @metadata[:described_type] = self.superclass.metadata && self.superclass.metadata[:described_type] else @metadata[:described_type] = args.shift end @metadata[:description] = args.shift || '' @metadata[:name] = "#{@metadata[:described_type]} #{@metadata[:description]}".strip extra_metadata.each do |k,v| @metadata[k] = v unless @metadata.has_key?(k) end @metadata[:file_path] = eval("caller(0)[0]", @metadata[:describe_block].binding) Micronaut.configuration.find_modules(self).each do |include_or_extend, mod, opts| if include_or_extend == :extend send(:extend, mod) unless extended_modules.include?(mod) else send(:include, mod) unless included_modules.include?(mod) end end end def self.metadata @metadata end def self.name @metadata[:name] end def self.described_type @metadata[:described_type] end def self.description @metadata[:description] end def self.describe(*args, &describe_block) raise(ArgumentError, "No arguments given. You must a least supply a type or description") if args.empty? raise(ArgumentError, "You must supply a block when calling describe") if describe_block.nil? subclass('NestedLevel') do args << {} unless args.last.is_a?(Hash) args.last.update(:describe_block => describe_block) set_it_up(*args) module_eval(&describe_block) end end def self.ancestors(superclass_last=false) classes = [] current_class = self while current_class < Micronaut::Behaviour superclass_last ? classes << current_class : classes.unshift(current_class) current_class = current_class.superclass end classes end def self.before_ancestors @_before_ancestors ||= ancestors end def self.after_ancestors @_after_ancestors ||= ancestors(true) end def self.eval_before_alls(example) Micronaut.configuration.find_before_or_after(:before, :all, self).each { |blk| example.instance_eval(&blk) } before_ancestors.each do |ancestor| ancestor.before_alls.each { |opts, blk| example.instance_eval(&blk) } end end def self.eval_before_eachs(example) Micronaut.configuration.find_before_or_after(:before, :each, self).each { |blk| example.instance_eval(&blk) } before_ancestors.each do |ancestor| ancestor.before_eachs.each { |opts, blk| example.instance_eval(&blk) } end end def self.eval_after_alls(example) Micronaut.configuration.find_before_or_after(:after, :all, self).each { |blk| example.instance_eval(&blk) } after_ancestors.each do |ancestor| ancestor.after_alls.each { |opts, blk| example.instance_eval(&blk) } end end def self.eval_after_eachs(example) Micronaut.configuration.find_before_or_after(:after, :each, self).each { |blk| example.instance_eval(&blk) } after_ancestors.each do |ancestor| ancestor.after_eachs.each { |opts, blk| example.instance_eval(&blk) } end end def self.run(reporter) return true if examples.size == 0 reporter.add_behaviour(self) group = new eval_before_alls(group) success = true examples_to_run.each do |ex| group.running_example = ex reporter.example_started(ex) execution_error = nil begin group._setup_mocks eval_before_eachs(group) if ex.example_block group.instance_eval(&ex.example_block) group._verify_mocks reporter.example_passed(ex) else reporter.example_pending(ex, 'Not yet implemented') end eval_after_eachs(group) rescue Exception => e reporter.example_failed(ex, e) execution_error ||= e ensure group._teardown_mocks end success &= execution_error.nil? end eval_after_alls(group) group.running_example = nil success end def self.subclass(base_name, &body) # :nodoc: @_sub_class_count ||= 0 @_sub_class_count += 1 klass = Class.new(self) class_name = "#{base_name}_#{@_sub_class_count}" const_set(class_name, klass) klass.instance_eval(&body) klass end def self.to_s self == Micronaut::Behaviour ? 'Micronaut::Behaviour' : name end end end