shoulda-matchers v3.0.0 Release Notes
Release Date: 2015-10-01 // over 8 years ago-
Backward-incompatible changes
๐ We've dropped support for Rails 3.x, Ruby 1.9.2, and Ruby 1.9.3, and RSpec 2. All of these have been end-of-lifed. (a4045a1, b7fe87a, 32c0e62)
โ The gem no longer detects the test framework you're using or mixes itself into that framework automatically. History has shown that performing any kind of detection is prone to bugs and more complicated than it should be.
Here are the updated instructions:
- You no longer need to say
require: false
in your Gemfile; you can include the gem as normal. You'll need to add the following somewhere in your
rails_helper
(for RSpec) ortest_helper
(for Minitest / Test::Unit):Shoulda::Matchers.configure do |config| config.integrate do |with| # Choose a test framework: with.test_framework :rspec with.test_framework :minitest with.test_framework :minitest_4 with.test_framework :test_unit # Choose one or more libraries: with.library :active_record with.library :active_model with.library :action_controller # Or, choose the following (which implies all of the above): with.library :rails end end
(1900071)
Previously, under RSpec, all of the matchers were mixed into all of the example groups. This created a problem because some gems, such as active_model_serializers-matchers, provide matchers that share the same name as some of our own matchers. Now, matchers are only mixed into whichever example group they belong to:
- ActiveModel and ActiveRecord matchers are available only in model example groups.
- ActionController matchers are available only in controller example groups.
- The
route
matcher is available only in routing example groups.
There are two changes to
allow_value
:- The negative form of
allow_value
has been changed so that instead of asserting that any of the given values is an invalid value (allowing good values to pass through), assert that all values are invalid values (allowing good values not to pass through). This means that this test which formerly passed will now fail:
expect(record).not_to allow_value('good value', *bad_values)
(19ce8a6)
allow_value
now raises a CouldNotSetAttributeError if in setting the attribute, the value of the attribute from reading the attribute back is different from the one used to set it.
This would happen if the writer method for that attribute has custom logic to ignore certain incoming values or change them in any way. Here are three examples we've seen:
- You're attempting to assert that an attribute should not allow nil, yet the attribute's writer method contains a conditional to do nothing if the attribute is set to nil:
class Foo include ActiveModel::Model attr_reader :bar def bar=(value) return if value.nil? @bar = value end end describe Foo do it do foo = Foo.new foo.bar = "baz" # This will raise a CouldNotSetAttributeError since `foo.bar` is now "123" expect(foo).not_to allow_value(nil).for(:bar) end end
- You're attempting to assert that an numeric attribute should not allow a string that contains non-numeric characters, yet the writer method for that attribute strips out non-numeric characters:
class Foo include ActiveModel::Model attr_reader :bar def bar=(value) @bar = value.gsub(/\D+/, '') end end describe Foo do it do foo = Foo.new # This will raise a CouldNotSetAttributeError since `foo.bar` is now "123" expect(foo).not_to allow_value("abc123").for(:bar) end end
- You're passing a value to
allow_value
that the model typecasts into another value:
describe Foo do # Assume that `attr` is a string # This will raise a CouldNotSetAttributeError since `attr` typecasts `[]` to `"[]"` it { should_not allow_value([]).for(:attr) } end
With all of these failing examples, why are we making this change? We want to guard you (as the developer) from writing a test that you think acts one way but actually acts a different way, as this could lead to a confusing false positive or negative.
If you understand the problem and wish to override this behavior so that you do not get a CouldNotSetAttributeError, you can add the
ignoring_interference_by_writer
qualifier like so. Note that this will not always cause the test to pass.it { should_not allow_value([]).for(:attr).ignoring_interference_by_writer }
(9d9dc4e)
- The negative form of
validate_uniqueness_of
is now properly case-sensitive by default, to match the default behavior of the validation itself. This is a backward-incompatible change because this test which incorrectly passed before will now fail:class Product < ActiveRecord::Base validates_uniqueness_of :name, case_sensitive: false end describe Product do it { is_expected.to validate_uniqueness_of(:name) } end
(57a1922)
ensure_inclusion_of
,ensure_exclusion_of
, andensure_length_of
have been removed in favor of theirvalidate_*
counterparts. (55c8d09)set_the_flash
andset_session
have been changed to more closely align with each other:Change
set_flash
so thatset_flash[:foo].now
is no longer valid syntax. You'll want to useset_flash.now[:foo]
instead. This was changed in order to more closely align with howflash.now
works when used in a controller. (#755, #752)Change behavior of
validate_uniqueness_of
when the matcher is not qualified with any scopes, but your validation is. Previously the following test would pass when it now fails:
class Post < ActiveRecord::Base validate :slug, uniqueness: { scope: :user_id } end describe Post do it { should validate_uniqueness_of(:slug) } end
(6ac7b81)
๐ Bug fixes
โ So far the tests for the gem have been running against only SQLite. Now they run against PostgreSQL, too. As a result we were able to fix some Postgres-related bugs, specifically around
validate_uniqueness_of
:- When scoped to a UUID column that ends in an "f", the matcher is able to generate a proper "next" value without erroring. (#402, #587, #662)
- Support scopes that are PostgreSQL array columns. Please note that this is only supported for Rails 4.2 and greater, as versions before this cannot handle array columns correctly, particularly in conjunction with the uniqueness validator. (#554)
- Fix so that when scoped to a text column and the scope is set to nil before running it through the matcher, the matcher does not fail. (#521, #607)
Fix
define_enum_for
so that it actually tests that the attribute is present in the list of defined enums, as you could fool it by merely defining a class method that was the pluralized version of the attribute name. In the same vein, passing a pluralized version of the attribute name todefine_enum_for
would erroneously pass, and now it fails. (#641)๐ Fix
permit
so that it does not break the functionality of ActionController::Parameters#require. (#648, #675)Fix
validate_uniqueness_of
+scoped_to
so that it does not raise an error if a record exists where the scoped attribute is nil. (#677)๐ Fix
route
matcher so if your route includes a defaultformat
, you can specify this as a symbol or string. (#693)Fix
validate_uniqueness_of
so that it allows you to test against scoped attributes that are boolean columns. (#457, #694)Fix failure message for
validate_numericality_of
as it sometimes didn't provide the reason for failure. (#699)๐ Fix
shoulda/matchers/independent
so that it can be required independently, without having to require all of the gem. (#746, e0a0200)
๐ Features
โ Add
on
qualifier topermit
. This allows you to make an assertion that a restriction was placed on a slice of theparams
hash and not the entireparams
hash. Although we don't require you to use this qualifier, we do recommend it, as it's a more precise check. (#675)Add
strict
qualifier tovalidate_numericality_of
. (#620)Add
on
qualifier tovalidate_numericality_of
. (9748869; h/t #356, #358)Add
join_table
qualifier tohave_and_belong_to_many
. (#556)allow_values
is now an alias forallow_value
. This makes more sense when checking against multiple values:
it { should allow_values('this', 'and', 'that') }
(#692)