Skip to content

Instantly share code, notes, and snippets.

@daronco
Created November 23, 2012 00:13
Show Gist options
  • Save daronco/4133411 to your computer and use it in GitHub Desktop.
Save daronco/4133411 to your computer and use it in GitHub Desktop.
Shoulda matcher have_attr_accessor
# Ensures the model has an attr_accessor, attr_reader or attr_writer
# Examples:
# it { should have_attr_accessor(:value) }
# it { should have_attr_accessor(:value).read_only }
# it { should have_attr_accessor(:value).write_only }
module Shoulda
module Matchers
module ActiveModel # :nodoc
def have_attr_accessor(attribute)
HaveAttrAccessorMatcher.new(attribute)
end
class HaveAttrAccessorMatcher < ValidationMatcher # :nodoc:
def initialize(attribute)
@attribute = attribute
@ro = false
@wo = false
end
def read_only
@ro = true
self
end
def write_only
@wo = true
self
end
def matches?(subject)
@subject = subject
reader = @subject.respond_to?(@attribute)
writer = @subject.respond_to?("#{@attribute.to_s}=")
v = true
v = v && !reader if @wo
v = v && reader unless @wo
v = v && !writer if @ro
v = v && writer unless @ro
v
end
def description
msg = "have "
if @ro
msg += "read only"
elsif @wo
msg += "write only"
else
msg += "read and write"
end
msg += " accessors for #{@attribute}"
end
def failure_message
if @ro
"Expected #{@subject.class.name} to respond only to '#{@attribute}'"
elsif @wo
"Expected #{@subject.class.name} to respond only to '#{@attribute}='"
else
"Expected #{@subject.class.name} to respond to both '#{@attribute}' and '#{@attribute}='"
end
end
def negative_failure_message
if @ro
"Expected #{@subject.class.name} not to respond only to '#{@attribute}'"
elsif @wo
"Expected #{@subject.class.name} not to respond only to '#{@attribute}'="
else
"Expected #{@subject.class.name} not to respond to '#{@attribute}' and '#{@attribute}='"
end
end
end
end
end
end
@joshuapinter
Copy link

Nice one. Any way you can submit this to the Shoulda library for inclusion?

@mattherick
Copy link

+1

@biinari
Copy link

biinari commented May 6, 2015

I would expect, instead of read_only and write_only, to have separate matchers for have_attr_writer and have_attr_reader. Then you could, for example, use attr_reader but create a more involved writer method that does not use attr_writer:

it { is_expected.to have_attr_reader(:value) }
it { is_expected.to_not have_attr_writer(:value) }

Also, attr_accessors are not part of ActiveModel, they are defined in ruby core Module

@HaleTom
Copy link

HaleTom commented Jan 10, 2016

Any updates on this? I find it hard to believe that people out there aren't testing their attr_*, or else are repeating themselves constantly.

@chrishough
Copy link

@smokinjoe
Copy link

All I get from this is a (NameError):

uninitialized constant Shoulda::Matchers::ActiveRecord::ValidationMatcher (NameError)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment