Module: Shoulda::ActiveRecord::Macros

Includes:
Matchers, Helpers
Included in:
Test::Unit::TestCase
Defined in:
lib/shoulda/active_record/macros.rb

Overview

Macro test helpers for your active record models

These helpers will test most of the validations and associations for your ActiveRecord models.

class UserTest < Test::Unit::TestCase
  should_validate_presence_of :name, :phone_number
  should_not_allow_values_for :phone_number, "abcd", "1234"
  should_allow_values_for :phone_number, "(123) 456-7890"

  should_not_allow_mass_assignment_of :password

  should_have_one :profile
  should_have_many :dogs
  should_have_many :messes, :through => :dogs
  should_belong_to :lover
end

For all of these helpers, the last parameter may be a hash of options.

Instance Method Summary collapse

Methods included from Matchers

#allow_mass_assignment_of, #allow_value, #belong_to, #ensure_inclusion_of, #ensure_length_of, #have_and_belong_to_many, #have_db_column, #have_db_index, #have_many, #have_named_scope, #have_one, #have_readonly_attribute, #validate_acceptance_of, #validate_format_of, #validate_numericality_of, #validate_presence_of, #validate_uniqueness_of

Methods included from Helpers

#report!

Instance Method Details

#should_allow_mass_assignment_of(*attributes) ⇒ Object

Ensures that the attribute can be set on mass update.

should_allow_mass_assignment_of :first_name, :last_name


83
84
85
86
87
88
89
90
91
92
# File 'lib/shoulda/active_record/macros.rb', line 83

def should_allow_mass_assignment_of(*attributes)
  get_options!(attributes)

  attributes.each do |attribute|
    matcher = allow_mass_assignment_of(attribute)
    should matcher.description do
      assert_accepts matcher, subject
    end
  end
end

#should_allow_values_for(attribute, *good_values) ⇒ Object

Ensures that the attribute can be set to the given values.

Example:

should_allow_values_for :isbn, "isbn 1 2345 6789 0", "ISBN 1-2345-6789-0"


149
150
151
152
153
154
155
156
157
# File 'lib/shoulda/active_record/macros.rb', line 149

def should_allow_values_for(attribute, *good_values)
  get_options!(good_values)
  good_values.each do |value|
    matcher = allow_value(value).for(attribute)
    should matcher.description do
      assert_accepts matcher, subject
    end
  end
end

#should_belong_to(*associations) ⇒ Object

Ensure that the belongs_to relationship exists.

should_belong_to :parent


335
336
337
338
339
340
341
342
343
# File 'lib/shoulda/active_record/macros.rb', line 335

def should_belong_to(*associations)
  dependent = get_options!(associations, :dependent)
  associations.each do |association|
    matcher = belong_to(association).dependent(dependent)
    should matcher.description do
      assert_accepts(matcher, subject)
    end
  end
end

#should_ensure_length_at_least(attribute, min_length, opts = {}) ⇒ Object

Ensures that the length of the attribute is at least a certain length

Options:

  • :short_message - value the test expects to find in errors.on(:attribute). Regexp or string. Default = I18n.translate('activerecord.errors.messages.too_short') % min_length

Example:

should_ensure_length_at_least :name, 3


194
195
196
197
198
199
200
201
202
203
204
# File 'lib/shoulda/active_record/macros.rb', line 194

def should_ensure_length_at_least(attribute, min_length, opts = {})
  short_message = get_options!([opts], :short_message)

  matcher = ensure_length_of(attribute).
    is_at_least(min_length).
    with_short_message(short_message)

  should matcher.description do
    assert_accepts matcher, subject
  end
end

#should_ensure_length_in_range(attribute, range, opts = {}) ⇒ Object

Ensures that the length of the attribute is in the given range

Options:

  • :short_message - value the test expects to find in errors.on(:attribute). Regexp or string. Default = I18n.translate('activerecord.errors.messages.too_short') % range.first

  • :long_message - value the test expects to find in errors.on(:attribute). Regexp or string. Default = I18n.translate('activerecord.errors.messages.too_long') % range.last

Example:

should_ensure_length_in_range :password, (6..20)


170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/shoulda/active_record/macros.rb', line 170

def should_ensure_length_in_range(attribute, range, opts = {})
  short_message, long_message = get_options!([opts], 
                                             :short_message,
                                             :long_message)
  matcher = ensure_length_of(attribute).
    is_at_least(range.first).
    with_short_message(short_message).
    is_at_most(range.last).
    with_long_message(long_message)

  should matcher.description do
    assert_accepts matcher, subject
  end
end

#should_ensure_length_is(attribute, length, opts = {}) ⇒ Object

Ensures that the length of the attribute is exactly a certain length

Options:

  • :message - value the test expects to find in errors.on(:attribute). Regexp or string. Default = I18n.translate('activerecord.errors.messages.wrong_length') % length

Example:

should_ensure_length_is :ssn, 9


215
216
217
218
219
220
221
222
223
224
# File 'lib/shoulda/active_record/macros.rb', line 215

def should_ensure_length_is(attribute, length, opts = {})
  message = get_options!([opts], :message)
  matcher = ensure_length_of(attribute).
    is_equal_to(length).
    with_message(message)

  should matcher.description do
    assert_accepts matcher, subject
  end
end

#should_ensure_value_in_range(attribute, range, opts = {}) ⇒ Object

Ensure that the attribute is in the range specified

Options:

  • :low_message - value the test expects to find in errors.on(:attribute). Regexp or string. Default = I18n.translate('activerecord.errors.messages.inclusion')

  • :high_message - value the test expects to find in errors.on(:attribute). Regexp or string. Default = I18n.translate('activerecord.errors.messages.inclusion')

Example:

should_ensure_value_in_range :age, (0..100)


237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/shoulda/active_record/macros.rb', line 237

def should_ensure_value_in_range(attribute, range, opts = {})
  message, low_message, high_message = get_options!([opts],
                                                    :message,
                                                    :low_message,
                                                    :high_message)
  matcher = ensure_inclusion_of(attribute).
    in_range(range).
    with_message(message).
    with_low_message(low_message).
    with_high_message(high_message)
  should matcher.description do
    assert_accepts matcher, subject
  end
end

#should_have_and_belong_to_many(*associations) ⇒ Object

Ensures that the has_and_belongs_to_many relationship exists, and that the join table is in place.

should_have_and_belong_to_many :posts, :cars


320
321
322
323
324
325
326
327
328
329
# File 'lib/shoulda/active_record/macros.rb', line 320

def should_have_and_belong_to_many(*associations)
  get_options!(associations)

  associations.each do |association|
    matcher = have_and_belong_to_many(association)
    should matcher.description do
      assert_accepts(matcher, subject)
    end
  end
end

#should_have_class_methods(*methods) ⇒ Object

Ensure that the given class methods are defined on the model.

should_have_class_methods :find, :destroy


349
350
351
352
353
354
355
356
357
# File 'lib/shoulda/active_record/macros.rb', line 349

def should_have_class_methods(*methods)
  get_options!(methods)
  klass = described_type
  methods.each do |method|
    should "respond to class method ##{method}" do
      assert_respond_to klass, method, "#{klass.name} does not have class method #{method}"
    end
  end
end

#should_have_db_columns(*columns) ⇒ Object Also known as: should_have_db_column

Ensure that the given columns are defined on the models backing SQL table. Also aliased to should_have_db_column for readability. Takes the same options available in migrations: :type, :precision, :limit, :default, :null, and :scale

Examples:

should_have_db_columns :id, :email, :name, :created_at

should_have_db_column :email,  :type => "string", :limit => 255
should_have_db_column :salary, :decimal, :precision => 15, :scale => 2
should_have_db_column :admin,  :default => false, :null => false


386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
# File 'lib/shoulda/active_record/macros.rb', line 386

def should_have_db_columns(*columns)
  column_type, precision, limit, default, null, scale, sql_type = 
    get_options!(columns, :type, :precision, :limit,
                          :default, :null, :scale, :sql_type)
  columns.each do |name|
    matcher = have_db_column(name).
                of_type(column_type).
                with_options(:precision => precision, :limit    => limit,
                             :default   => default,   :null     => null,
                             :scale     => scale,     :sql_type => sql_type)
    should matcher.description do
      assert_accepts(matcher, subject)
    end
  end
end

#should_have_db_indices(*columns) ⇒ Object Also known as: should_have_db_index

Ensures that there are DB indices on the given columns or tuples of columns. Also aliased to should_have_db_index for readability

Options:

  • :unique - whether or not the index has a unique constraint. Use true to explicitly test for a unique constraint. Use false to explicitly test for a non-unique constraint. Use nil if you don’t care whether the index is unique or not. Default = nil

Examples:

should_have_db_indices :email, :name, [:commentable_type, :commentable_id]
should_have_db_index :age
should_have_db_index :ssn, :unique => true


420
421
422
423
424
425
426
427
428
429
# File 'lib/shoulda/active_record/macros.rb', line 420

def should_have_db_indices(*columns)
  unique = get_options!(columns, :unique)
  
  columns.each do |column|
    matcher = have_db_index(column).unique(unique)
    should matcher.description do
      assert_accepts(matcher, subject)
    end
  end
end

#should_have_index(*args) ⇒ Object

Deprecated. See should_have_db_index



434
435
436
437
438
# File 'lib/shoulda/active_record/macros.rb', line 434

def should_have_index(*args)
  warn "[DEPRECATION] should_have_index is deprecated. " <<
       "Use should_have_db_index instead."
  should_have_db_index(*args)
end

#should_have_indices(*args) ⇒ Object

Deprecated. See should_have_db_indices



441
442
443
444
445
# File 'lib/shoulda/active_record/macros.rb', line 441

def should_have_indices(*args)
  warn "[DEPRECATION] should_have_indices is deprecated. " <<
       "Use should_have_db_indices instead."
  should_have_db_indices(*args)
end

#should_have_instance_methods(*methods) ⇒ Object

Ensure that the given instance methods are defined on the model.

should_have_instance_methods :email, :name, :name=


363
364
365
366
367
368
369
370
371
# File 'lib/shoulda/active_record/macros.rb', line 363

def should_have_instance_methods(*methods)
  get_options!(methods)
  klass = described_type
  methods.each do |method|
    should "respond to instance method ##{method}" do
      assert_respond_to klass.new, method, "#{klass.name} does not have instance method #{method}"
    end
  end
end

#should_have_many(*associations) ⇒ Object

Ensures that the has_many relationship exists. Will also test that the associated table has the required columns. Works with polymorphic associations.

Options:

  • :through - association name for has_many :through

  • :dependent - tests that the association makes use of the dependent option.

Example:

should_have_many :friends
should_have_many :enemies, :through => :friends
should_have_many :enemies, :dependent => :destroy


285
286
287
288
289
290
291
292
293
# File 'lib/shoulda/active_record/macros.rb', line 285

def should_have_many(*associations)
  through, dependent = get_options!(associations, :through, :dependent)
  associations.each do |association|
    matcher = have_many(association).through(through).dependent(dependent)
    should matcher.description do
      assert_accepts(matcher, subject)
    end
  end
end

#should_have_named_scope(scope_call, find_options = nil) ⇒ Object

Deprecated.

Ensures that the model has a method named scope_name that returns a NamedScope object with the proxy options set to the options you supply. scope_name can be either a symbol, or a method call which will be evaled against the model. The eval’d method call has access to all the same instance variables that a should statement would.

Options: Any of the options that the named scope would pass on to find.

Example:

should_have_named_scope :visible, :conditions => {:visible => true}

Passes for

named_scope :visible, :conditions => {:visible => true}

Or for

def self.visible
  scoped(:conditions => {:visible => true})
end

You can test lambdas or methods that return ActiveRecord#scoped calls:

should_have_named_scope 'recent(5)', :limit => 5
should_have_named_scope 'recent(1)', :limit => 1

Passes for

named_scope :recent, lambda {|c| {:limit => c}}

Or for

def self.recent(c)
  scoped(:limit => c)
end


504
505
506
507
508
509
# File 'lib/shoulda/active_record/macros.rb', line 504

def should_have_named_scope(scope_call, find_options = nil)
  matcher = have_named_scope(scope_call).finding(find_options)
  should matcher.description do
    assert_accepts matcher.in_context(self), subject
  end
end

#should_have_one(*associations) ⇒ Object

Ensure that the has_one relationship exists. Will also test that the associated table has the required columns. Works with polymorphic associations.

Options:

  • :dependent - tests that the association makes use of the dependent option.

Example:

should_have_one :god # unless hindu


305
306
307
308
309
310
311
312
313
# File 'lib/shoulda/active_record/macros.rb', line 305

def should_have_one(*associations)
  dependent, through = get_options!(associations, :dependent, :through)
  associations.each do |association|
    matcher = have_one(association).dependent(dependent).through(through)
    should matcher.description do
      assert_accepts(matcher, subject)
    end
  end
end

#should_have_readonly_attributes(*attributes) ⇒ Object

Ensures that the attribute cannot be changed once the record has been created.

should_have_readonly_attributes :password, :admin_flag


113
114
115
116
117
118
119
120
121
122
# File 'lib/shoulda/active_record/macros.rb', line 113

def should_have_readonly_attributes(*attributes)
  get_options!(attributes)

  attributes.each do |attribute|
    matcher = have_readonly_attribute(attribute)
    should matcher.description do
      assert_accepts matcher, subject
    end
  end
end

#should_not_allow_mass_assignment_of(*attributes) ⇒ Object

Ensures that the attribute cannot be set on mass update.

should_not_allow_mass_assignment_of :password, :admin_flag


98
99
100
101
102
103
104
105
106
107
# File 'lib/shoulda/active_record/macros.rb', line 98

def should_not_allow_mass_assignment_of(*attributes)
  get_options!(attributes)

  attributes.each do |attribute|
    matcher = allow_mass_assignment_of(attribute)
    should "not #{matcher.description}" do
      assert_rejects matcher, subject
    end
  end
end

#should_not_allow_values_for(attribute, *bad_values) ⇒ Object

Ensures that the attribute cannot be set to the given values

Options:

  • :message - value the test expects to find in errors.on(:attribute). Regexp or string. If omitted, the test will pass if there is ANY error in errors.on(:attribute).

Example:

should_not_allow_values_for :isbn, "bad 1", "bad 2"


134
135
136
137
138
139
140
141
142
# File 'lib/shoulda/active_record/macros.rb', line 134

def should_not_allow_values_for(attribute, *bad_values)
  message = get_options!(bad_values, :message)
  bad_values.each do |value|
    matcher = allow_value(value).for(attribute).with_message(message)
    should "not #{matcher.description}" do
      assert_rejects matcher, subject
    end
  end
end

#should_validate_acceptance_of(*attributes) ⇒ Object

Ensures that the model cannot be saved if one of the attributes listed is not accepted.

Options:

  • :message - value the test expects to find in errors.on(:attribute). Regexp or string. Default = I18n.translate('activerecord.errors.messages.accepted')

Example:

should_validate_acceptance_of :eula


456
457
458
459
460
461
462
463
464
465
# File 'lib/shoulda/active_record/macros.rb', line 456

def should_validate_acceptance_of(*attributes)
  message = get_options!(attributes, :message)

  attributes.each do |attribute|
    matcher = validate_acceptance_of(attribute).with_message(message)
    should matcher.description do
      assert_accepts matcher, subject
    end
  end
end

#should_validate_numericality_of(*attributes) ⇒ Object

Ensure that the attribute is numeric

Options:

  • :message - value the test expects to find in errors.on(:attribute). Regexp or string. Default = I18n.translate('activerecord.errors.messages.not_a_number')

Example:

should_validate_numericality_of :age


261
262
263
264
265
266
267
268
269
270
# File 'lib/shoulda/active_record/macros.rb', line 261

def should_validate_numericality_of(*attributes)
  message = get_options!(attributes, :message)
  attributes.each do |attribute|
    matcher = validate_numericality_of(attribute).
      with_message(message)
    should matcher.description do
      assert_accepts matcher, subject
    end
  end
end

#should_validate_presence_of(*attributes) ⇒ Object

Ensures that the model cannot be saved if one of the attributes listed is not present.

Options:

  • :message - value the test expects to find in errors.on(:attribute). Regexp or string. Default = I18n.translate('activerecord.errors.messages.blank')

Example:

should_validate_presence_of :name, :phone_number


35
36
37
38
39
40
41
42
43
44
# File 'lib/shoulda/active_record/macros.rb', line 35

def should_validate_presence_of(*attributes)
  message = get_options!(attributes, :message)

  attributes.each do |attribute|
    matcher = validate_presence_of(attribute).with_message(message)
    should matcher.description do
      assert_accepts(matcher, subject)
    end
  end
end

#should_validate_uniqueness_of(*attributes) ⇒ Object

  • :message - value the test expects to find in errors.on(:attribute). Regexp or string. Default = I18n.translate('activerecord.errors.messages.taken')

  • :scoped_to - field(s) to scope the uniqueness to.

  • :case_sensitive - whether or not uniqueness is defined by an exact match. Ignored by non-text attributes. Default = true

Examples:

should_validate_uniqueness_of :keyword, :username
should_validate_uniqueness_of :name, :message => "O NOES! SOMEONE STOELED YER NAME!"
should_validate_uniqueness_of :email, :scoped_to => :name
should_validate_uniqueness_of :address, :scoped_to => [:first_name, :last_name]
should_validate_uniqueness_of :email, :case_sensitive => false


64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/shoulda/active_record/macros.rb', line 64

def should_validate_uniqueness_of(*attributes)
  message, scope, case_sensitive = get_options!(attributes, :message, :scoped_to, :case_sensitive)
  scope = [*scope].compact
  case_sensitive = true if case_sensitive.nil?

  attributes.each do |attribute|
    matcher = validate_uniqueness_of(attribute).
      with_message(message).scoped_to(scope)
    matcher = matcher.case_insensitive unless case_sensitive
    should matcher.description do
      assert_accepts(matcher, subject)
    end
  end
end