Post outline

UPDATE: I created a gem to cover the functionality of this post. Visit its homepage here.

I was working on my application and I wanted to write some RSpec tests for some controllers having before_filter for some actions.

I didn’t want to explicitly test the behaviour of the filter since it was already tested in another context. I only wanted to test that it is being executed.

Let’s assume that this is our controller:

class FooController < ApplicationController
  before_filter :first_filter
  before_filter :second_filter, :only => [:edit, :update]

  def index
    # some code
  end

  def edit
    #some code
  end

  def update
    # some code
  end

  protected

  def first_filter
    # do something
  end

  def second_filter
    # do something
  end
end

So, we can write the following custom matcher (I placed it under spec/support/matchers/filters.rb):

RSpec::Matchers.define :execute_before_filter do |filter_name, options|
  match do |controller|
    controller.stubs(filter_name).raises(StandardError.new("Filter executed: #{filter_name}"))

    if options[:stub_filters]
      options[:stub_filters].each do |filter|
        controller.stubs(filter).returns(true)
      end
    end

    result = begin
      send(options[:via], options[:on], options[:with])
      false
    rescue StandardError => e
      e.message == "Filter executed: #{filter_name}"
    rescue
      false
    end
    result
  end

  failure_message do |actual|
    filter = expected[0]
    options = expected[1]
    action = options[:on]
    with = options[:via]
    params = options[:with]
    message = "expected #{actual.class} to execute filter #{filter}"
    message << " before action #{action}"
    message << " [requested via #{with} with params '#{params}']."
  end

  failure_message_when_negated do |actual|
    filter = expected[0]
    options = expected[1]
    action = options[:on]
    with = options[:via]
    params = options[:with]
    message = "expected #{actual.class} not to execute filter #{filter}"
    message << " before action #{action}"
    message << " [requested via #{with} with params '#{params}']"
  end
end

and then in our controller’s spec we test the filter execution with the following code:

require 'rails_helper'

RSpec.describe FooController, :type => :controller do

  it { should execute_before_filter :first_filter, :on => :index, :via => :get }

  it { should execute_before_filter :first_filter, :on => :edit, :via => :get, :with => { :id => '1' } }
  it { should execute_before_filter :second_filter,
                                    :on => :edit,
                                    :via => :get,
                                    :with => { :id => '1' },
                                    :stub_filters => [:first_filter] }

  it { should execute_before_filter :first_filter, :on => :update, :via => :put, :with => { :id => '1' } }
  it { should execute_before_filter :second_filter,
                                    :on => :update,
                                    :via => :put,
                                    :with => { :id => :'1' },
                                    :stub_filters => [:first_filter] }
end

We use the :ignore_filters to stub possible prepended filters so that in case they brake due to missing stubbings etc, they don’t mess with our test.

If we comment the before_filter declarations in the FooController => tadaaaaa:

My news

I created an open source development tool named DuckRails for mocking API endpoints quickly and dynamically.

Version 2.1.2 is out and it is now possible to run the application via its docker image.

Star


Ruby does Gtk
I published a tutorial for creating a simple Linux Desktop app with Ruby.