views:

583

answers:

2

OK, so instead of writing a whole bunch of access control specs, and duplicating them across many of my spec files, I'm looking to create a custom matcher. So instead of this:

describe "access control" do
  it "should prevent access by non-logged-in users"
  it "should prevent access by normal users"
  it "should prevent access by editor users"
  it "should prevent access by admin users"
  it "should allow access by super admin users"
end

I want do something like this:

lambda do
  get :index
end.should have_access_control(:allowed => [:super_admin], :disallowed => [:admin, :editor, :user])

Are there any examples or suggestions of how I can go about doing something like this?

+2  A: 

OK, I have found a method of achieving this, though it doesn't use a custom matcher. Include the following code in your spec_helper.rb:

def access_control (code, options={})
  options = {:allow => [], :disallow => []}.merge(options)

  options[:allow].each do |user|
    it "#{code} should allow #{user.to_s}" do
      login_as(user)
      eval code
      response.should_not redirect_to(login_path)
    end
  end

  options[:disallow].each do |user|
    it "#{code} should disallow #{user.to_s}" do
      login_as(user)
      eval code
      response.should redirect_to(login_path)
    end
  end
end

And call it as follows:

access_control("get :index", {:allow => [:super_admin], :disallow => [:quentin, :admin]})

You can then use it to build up a complete list of methods that should be restricted, and the users to which they are restricted to.

Mr. Matt
A: 

I disagree with your solution. Tests shouldn't be an area to factor out duplication like this. It makes the tests harder to read and harder to maintain. Also, there's certainly an argument to be made that duplication in tests can inform your design.

In the initial example you listed, there are 5 different contexts that should each be tested in isolation and able to be understood at a glance. Your solution, while neat, harms these two goals.

nakajima
That's be fine if the function obfuscated the intent of the test, however, the function produces separate test cases for each intent so that, on failure, each failing test is detailed separately.
Mr. Matt
Furthermore, the function call is far more concise (instead of having six lines per role per action bulking up the spec).
Mr. Matt