Module: ChefSpec::Cacher

Defined in:
lib/chefspec/cacher.rb

Overview

The cacher module allows for ultra-fast tests by caching the results of a CCR in memory across an example group. In testing, this can reduce the total testing time by a factor of 10x. This strategy is not the default behavior, because it has implications surrounding stubbing and is not threadsafe!

The credit for this approach and code belongs to Juri Timošin (DracoAter). Please see his original blog post below for an in-depth explanation of how and why this approach is faster.

Examples:

Using the Cacher module

First, require the Cacher module in your +spec_helper.rb+:

  RSpec.configure do |config|
    config.extend(ChefSpec::Cacher)
  end

Next, change your +let+ blocks to +cached+ blocks:

  let(:chef_run) { ... } #=> cached(:chef_run) { ... }

Finally, celebrate!

See Also:

Constant Summary collapse

FINALIZER =
lambda { |id| @@cache.delete(id) }
@@cache =
{}

Instance Method Summary collapse

Instance Method Details

#cached(name, &block) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/chefspec/cacher.rb', line 36

def cached(name, &block)
  location = ancestors.first.[:location]
  unless location.nil?
    location += ancestors.first.[:description] unless ancestors.first.[:description].nil?
    location += ancestors.first.[:scoped_id] unless ancestors.first.[:scoped_id].nil?
  end
  location ||= ancestors.first.[:parent_example_group][:location]

  define_method(name) do
    key = [location, name.to_s].join(".")
    unless @@cache.key?(Thread.current.object_id)
      ObjectSpace.define_finalizer(Thread.current, FINALIZER)
    end
    @@cache[Thread.current.object_id] ||= {}
    @@cache[Thread.current.object_id][key] ||= instance_eval(&block)
  end
end

#cached!(name, &block) ⇒ Object



54
55
56
57
58
# File 'lib/chefspec/cacher.rb', line 54

def cached!(name, &block)
  cached(name, &block)

  before { send(name) }
end