---
title: Shoulda Matchers 2.6.0
teaser: |
  Announcing the latest version of Shoulda Matchers,
  now ready for Rails 4.1.
tags: news,web,ruby,open source,testing,shoulda
author: Elliot Winkler
published_on: 2014-04-14
---

For the past four months, we've been hard at work on the next version of
[shoulda-matchers][shoulda-matchers], and we're pleased to announce it's up on
[RubyGems][rubygems].

2.6.0 is first and foremost a compatibility release, bringing a few of the
matchers up to date with the newest changes in Rails 4.1. However, there are
also some new matchers for you to use.

The following is a brief highlight of the most important changes; there are
plenty of additional fixes and additions we made, so [read the NEWS file][news]
for the full scoop.

## Matchers restored from 2.0.0

If you remember, when we released 2.0.0, we decided to remove a couple of
matchers as we hadn't quite polished them enough. They're now back!

You can now use the `permit` matcher for testing usage of strong parameters in
your controller:

```ruby
class UserController < ActionController::Base
  def create
    User.create(user_params)
  end

  private

  def user_params
    params.require(:user).permit(:email)
  end
end

# RSpec
describe UserController do
  it { should permit(:email).for(:create) }
end

# Test::Unit
class UserControllerTest < ActionController::TestCase
  should permit(:email).for(:create)
end
```

You can also use `delegate_method` for usage of Rails's `delegate` method or
other forms of delegation:

```ruby
class Human < ActiveRecord::Base
  has_one :robot
  delegate :work, to: :robot

  # alternatively, if you are not using Rails
  def work
    robot.work
  end

  def protect
    robot.protect('Sarah Connor')
  end

  def speak
    robot.beep_boop
  end
end

# RSpec
describe Human do
  it { should delegate_method(:work).to(:robot) }
  it { should delegate_method(:protect).to(:robot).with_arguments('Sarah Connor') }
  it { should delegate_method(:beep_boop).to(:robot).as(:speak) }
end

# Test::Unit
class HumanTest < ActiveSupport::TestCase
  should delegate_method(:work).to(:robot)
  should delegate_method(:protect).to(:robot).with_arguments('Sarah Connor')
  should delegate_method(:beep_boop).to(:robot).as(:speak)
end
```

## Callback matchers

There are also new matchers for testing callbacks in controllers:

* `use_before_filter`
* `use_after_filter`
* `use_around_filter`

These are aliased as `use_<callback>_action`, for consistency with the renamed
callback macros in Rails 4. Here's an example:

```ruby
class UserController < ActionController::Base
  before_filter :authenticate_user!
end

# RSpec
describe UserController do
  it { should use_before_filter(:authenticate_user!) }
end

# Test::Unit
class UserControllerTest < ActionController::TestCase
  should use_before_filter(:authenticate_user!)
end
```

## validate_presence_of + has_secure_password could raise an error

We received a [report][issue-383] that under Rails 4.1, `validate_presence_of`
could fail when used against a record with an already set `password` which is part
of a model that `has_secure_password`:

```ruby
class User < ActiveRecord::Base
  has_secure_password validations: false
  validate_presence_of :password
end

describe User do
  it do
    user = User.new(password: 'whatever')

    # This would fail with a message like:
    # Expected "can't be blank" on password, got no errors
    user.should validate_presence_of(:password)
  end
end
```

Why does this fail?

`validate_presence_of` works by setting the attribute in question to nil,
calling #valid? on the record, and then asserting that an "is blank" error
message appears on that attribute.

In Rails 4.1, `has_secure_password` [was changed][has-secure-password] so that
`password` cannot be set to nil. Therefore, if, in your test, `password` is
already set to a value before `validate_presence_of` is called, `password` will
never get unset. Therefore it is impossible to test that errors occur when the
validation is violated, so using `validate_presence_of` will now fail to match.
In order to highlight why the match fails, we [capture the failure as a
CouldNotSetPasswordError exception][could-not-set-password]:

```ruby
describe User do
  it do
    user = User.new(password: 'whatever')

    # This now raises an exception:
    # Shoulda::Matchers::ActiveModel::CouldNotSetPasswordError
    user.should validate_presence_of(:password)
  end
end
```

**What can you do to fix this?** Call `validate_presence_of` on an empty record
instead:

```ruby
describe User do
  it do
    user = User.new
    user.should validate_presence_of(:password)
  end
end
```

## allow_value could raise an error

While we investigated the issue referred to above, we realized it revealed a
larger issue. The `allow_value` matcher also takes the same approach as
`validate_presence_of`, only more generically. That is, it works by taking the
value you provide, setting it on the attribute in question, running validations,
and then asserting that there are no errors on that attribute.

This means that it's also subject to the same problem as `validate_presence_of`:
if the attribute somehow prevents the value from being set, this will interfere
with the assertion that `allow_value` is making, because now the test will be
using a different value than you specified. In this case, [we raise an
CouldNotClearAttribute exception][could-not-clear-attribute].

This change could affect you particularly if you are using `should_not
allow_value`. For example:

```ruby
class Issue < ActiveRecord::Base
  validates_presence_of :status

  def status=(value)
    super(value || 'created')
  end
end

describe Issue do
  it { should_not allow_value(nil).for(:status) }
end
```

Prior to 2.6.0, this test would have passed. While the test as written is
technically correct, the test as executed is not. Setting `status` to nil
actually results in `status` being set to `"created"`. Therefore, the test above
is actually equivalent to the following:

```ruby
describe Issue do
  it { should_not allow_value('created').for(:status) }
end
```

Obviously, this is fundamentally a different test than the above. Should this
new test pass? In this case, it does. But since `allow_value` is generic, it
depends on whatever validation you've placed on your attribute. We don't
know what that is from the perspective of the matcher, so in 2.6.0, we raise an
exception and let you specify more of what you want.

**What can you do about this?** Again, it depends on validations you may have
present. In our example above, we actually don't need the presence validation
since it will never fail, and that means we can get rid of our `allow_value`
test. You may have to take a different approach in order to resolve this.

## ensure_inclusion_of now works with boolean columns (with caveats)

[This issue][issue-291] has evaded us for a while. [The
fix][ensure-inclusion-of] was not straightforward and we ultimately ended up
making a couple of compromises.

One case the fix addressed was testing that a boolean column accepts true or
false or both. If you say that a boolean column only accepts true, you have
to test that it can't accept false; if you specify that it only accepts
false, you have to test that it can't accept true. That part's easy:

```ruby
class Issue < ActiveRecord::Base
  validates_inclusion_of :open, in: [true]
  validates_inclusion_of :closed, in: [false]
end

describe Issue do
  it { should ensure_inclusion_of(:open).in_array([true])
  it { should ensure_inclusion_of(:false).in_array([false])
end
```

But what if a column can accept both true and false? What do you test?
Well, there's no value that a boolean column can take other than true and false,
so this sort of test is impossible to write:

```ruby
class Issue < ActiveRecord::Base
  validates_inclusion_of :open, in: [true, false]
end

describe Issue do
  # This will work, but will give you a warning
  it { should ensure_inclusion_of(:open).in_array([true, false])
end
```

The other case addressed by this fix occurs if you have a boolean column which
is non-nullable and you test that the column should accept nil as a value.
This is also an impossible test because a non-nullable column, by definition,
cannot accept nil; and because it's a boolean column in this case, as soon as
you set it to nil it will get coerced to false.

```ruby
create_table :issues do |t|
  t.boolean :open, null: false
end

describe Issue do
  # This will also work, but will also give you a warning
  it { should_not ensure_inclusion_of(:open).in_array([nil])
end
```

## Other notable features and bugfixes

* Association matchers gained the `autosave` option.
* `validate_numericality_of` gained the `allow_nil` option.
* `belong_to` gained the `inverse_of` option.
* `rescue_from` now works with exception handler methods marked as protected or
  private.

## Thank you

I'd like to thank the contributors that helped us with this release:

* [Mauro George][maurogeorge] for your work on Rails 4.1 support
* Fellow tb'ers [Damian][dgalarza], [Harry][hrs], [Mason][masonforest] and
  [Anthony][anthonynavarre] for adding `permit`, `delegate_method`, the callback
  matchers, and for fixing `validates_uniqueness_of` so it works alongside
  `has_secure_password`
* [Yukio Mizuta][untidy-hair], [Chulki Lee][chulkilee], [Andy Chambers][cddr],
  [Daniel Morris][unfunco], [Dmitry Tonkonogov][tonkonogov],
  [petedmarsh][petedmarsh], and [pikachuEXE][pikachuEXE] for features and
  bugfixes

Enjoy!

[shoulda-matchers]: https://github.com/thoughtbot/shoulda-matchers
[rubygems]: http://rubygems.org/gems/shoulda-matchers
[dgalarza]: https://github.com/dgalarza
[masonforest]: https://github.com/masonforest
[hrs]: https://github.com/hrs
[anthonynavarre]: https://github.com/anthonynavarre
[maurogeorge]: https://github.com/maurogeorge
[untidy-hair]: https://github.com/untidy-hair
[chulkilee]: https://github.com/chulkilee
[cddr]: https://github.com/cddr
[unfunco]: https://github.com/unfunco
[tonkonogov]: https://github.com/tonkonogov
[petedmarsh]: https://github.com/petedmarsh
[pikachuEXE]: https://github.com/pikachuEXE
[news]: https://github.com/thoughtbot/shoulda-matchers/blob/master/NEWS.md
[issue-291]: https://github.com/thoughtbot/shoulda-matchers/issues/291
[issue-383]: https://github.com/thoughtbot/shoulda-matchers/issues/383
[has-secure-password]: https://github.com/rails/rails/blob/b5e9027bf2b3bdf3cafd2f1d5044caeea2065619/activemodel/lib/active_model/secure_password.rb#L102
[could-not-set-password]: https://github.com/thoughtbot/shoulda-matchers/commit/6173a160ac334fd66cda60555e348d5136b08e01#diff-bea52ec23e2aa93bc1228ec91f494c18R34
[could-not-clear-attribute]: https://github.com/thoughtbot/shoulda-matchers/commit/6173a160ac334fd66cda60555e348d5136b08e01#diff-3034efc115d2982d746ac92c8765157eR109
[ensure-inclusion-of]: https://github.com/thoughtbot/shoulda-matchers/commit/5b44edf8b31006724b2864037002f73c09e608cf
