Skip to content

Instantly share code, notes, and snippets.

@cupakromer
Last active August 29, 2015 13:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cupakromer/9149496 to your computer and use it in GitHub Desktop.
Save cupakromer/9149496 to your computer and use it in GitHub Desktop.
Advanced RSpec Hook Inclusion

Check the Metadata

Say you normally use the great database_cleaner gem. Per the README the standard setup is:

RSpec.configure do |config|

  config.before(:suite) do
    DatabaseCleaner.strategy = :transaction
    DatabaseCleaner.clean_with(:truncation)
  end

  config.around(:each) do |example|
    DatabaseCleaner.cleaning do
      example.run
    end
  end

end

However, you have a subset of specs which you wish to not have the cleaner handle. By checking the metadata in the around hook, you can conditionally run the cleaner.

RSpec.configure do |config|

  # Other setup
  
  # RSpec 2 Metadata Config (Not necessary with RSpec 3)
  # Ref https://www.relishapp.com/rspec/rspec-core/v/2-14/docs/metadata/user-defined-metadata
  config.treat_symbols_as_metadata_keys_with_true_values = true

  config.around do |example|
    # BROKEN!
    unless example.metadata[:no_database_cleaner]
      DatabaseCleaner.cleaning(&example)
    end
  end

end


# Your spec file
require "spec_helper"

describe "Mystery database state", :no_database_cleaner do

  it "does not use the cleaning process" do
  end

end

Use a Custom Module with Default Metadata

Say your setup is a bit more complicated. Additionally, you've extracted the logic into a re-useable module which you share across different projects. By setting default metadata, and leveraging hook filters you can accomplish the same goal:

module DefaultDatabaseCleaning

  # Your complex stuff
  
  def self.included(klass)
    # Other possible setup
    
    klass.class_exec do
      # Both `nil` and `false` are falsey, so we explicitly check for only
      # `nil`; otherwise a user specificaly set `false`
      metadata[:clean_database] = true if metadata[:clean_database].nil?
      
      # Here we need to explicitly pass :each so we can set the filter
      around :each, :clean_database do |example|
        DatabaseCleaner.cleaning(&example)
      end
    end
  end

end


# spec/spec_helper.rb
RSpec.configure do |config|

  config.include DefaultDatabaseCleaning

end


# Your Spec file
require "spec_helper"

describe "Leveraging default metadata" do

  it "default setting it to use cleaning" do
  end
  
  it "no cleaning here please", clean_database: false do
  end

end

Combinating Techniques

By combining the techniques and leveraging the fact that symbols are truthy. The metadata could pull double duty:

module DefaultDatabaseCleaning

  def self.included(klass)
    klass.class_exec do
      metadata[:clean_database] = :transaction if metadata[:clean_database].nil?
      
      around do |example|
        # BROKEN!
        if example.metadata[:clean_database]
          DatabaseCleaner.strategy = example.metadata[:clean_database]
          DatabaseCleaner.cleaning(&example)
        end
      end
    end
  end

end


# Your Spec file
require "spec_helper"

describe "Pulling double duty" do

  context "Myster database state", clean_database: false do
    it "does not use the cleaning process" {}
  end
  
  context "Using Redis here instead", clean_database: :truncation do
    it "uses the truncation strategy" {}
  end
  
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment