Commit Graph

15 Commits

Author SHA1 Message Date
Elliot Winkler 707d87dbd6 Add without_presence_validation q to belong_to
With the new Rails 5 behavior, `belong_to` will check to ensure that the
association has a presence validation on it. In some cases, however,
this is not desirable. For instance, say we have this setup:

    class Employee < ApplicationRecord
      # Assume belongs_to_required_by_default is true
      belongs_to :manager

      before_validation :add_manager

      private

      def add_manager
        self.manager = Manager.create
      end
    end

In this case, even though the association is effectively defined with
`required: true`, the ensuing presence validation never fails, because
`manager` is always set to something before validations kick off. So
this test won't work:

    it { should belong_to(:manager) }

To get around this, this commit allows us to say:

    it { should belong_to(:manager).without_presence_validation }

which instructs the matcher not to test for any presence (or absence,
for that matter) of a presence validation, mimicking the pre-Rails 5
behavior.
2019-02-14 17:48:26 -07:00
Elliot Winkler ca6a1ff9ee Improve messaging of .required and .optional
When the `required` and `optional` qualifiers on `belong_to` fail, the
message can be a little confusing. There are actually two ways to
satisfy both qualifiers. One way, of course, is to match the option on
the association with the qualifier itself, so if the association is
defined with `required: true`, that will match
`belong_to(...).required`, and if it's `required: false`, that will
match `belong_to(...).required(false)` (and similar for `optional`). The
second way, though, is to manually add a presence validation to the
association (this will obviously only match `required(true)`). We need
to update the failure message to reflect these cases.
2019-02-14 17:48:26 -07:00
Elliot Winkler a465e796e8 Fix default behavior of belong_to under Rails 4.2
Rails 5 introduced a change where `belongs_to` would default to adding a
presence validation along with the association. However, it also
introduced a configuration option, `belongs_to_required_by_default`, to
emulate the old behavior prior to Rails 5. For Rails 4.2 projects as
well as Rails 5 which were migrated from 4, this setting is false, so
that existing apps do not break.

To mimic this, a change was made to the `belong_to` matcher to check for
the presence of the presence validator if
`belongs_to_required_by_default` is true and check for the absence of
the presence validator if it is false. However, this last bit of the
logic actually causes problems. Take this case, for example:

    ActiveRecord::Base.belongs_to_required_by_default = false

    class Post < ActiveRecord::Base
      belongs_to :user
      validates :user, presence: true
    end

    RSpec.describe Post, type: :model do
      it { is_expected.to belong_to(:user) }
    end

In this example, the developer has chosen to place a presence validation
on the association manually. `belong_to` doesn't know this, however, and
will check to make sure that `user` can be nil, which of course it
can't. Therefore, this test will fail. In addition, the failure message
that `belong_to` generates is confusing:

    Expected Post to have a belongs_to association called user (the
    association should have been defined with `optional: true`, but was
    not)

The reason why the test fails is that when
`belongs_to_required_by_default` is false, belong_to` will place an
implicit `optional` qualifier on itself. In other words, these two tests
are equivalent:

    it { is_expected.to belong_to(:user) }
    it { is_expected.to belong_to(:user).optional }

However, this is not only wrong, but the `belongs_to` macro in Rails 4.2
doesn't have an `optional` option (it has `required` instead), so the
failure message that `belong_to` generates is confusing.

This commit fixes this by modifying `belong_to` so that under Rails 4.2,
the matcher will have not have any qualifiers on it by default.
2018-09-16 12:21:41 -06:00
Brendan Thomas 795ca688bf add index_errors association matcher 2018-04-04 14:29:08 -06:00
Elliot Winkler cd96089a56 belongs_to: Change default req/opt to match global default
In Rails 5, belongs_to associations default to `required: true`. This is
configurable by setting
`ActiveRecord::Base.belongs_to_required_by_default` to true. In new
Rails 5 apps, this is set to true, because the Rails generator will add
an initializer with the following line in it:

  config.active_record.belongs_to_required_by_default = true

However, for Rails apps that have been upgraded from 4 to 5, this
initializer may not be present, and in that case, that setting will not
be set, and `belong_to` associations will not default to `required:
true`.

This means that under Rails 5, our `belong_to` matcher cannot always
default to applying the `required` qualifier; it must abide by the
`belongs_to_required_by_default` setting in doing so.
2018-01-29 00:03:01 -06:00
Elliot Winkler 3af3d9f7ab Add required/optional qual's to belongs_to/has_one
Rails 5 made two changes to `belongs_to` associations:

* `required` and `optional` were added as options (which add and remove
  a presence validation on the association, respectively)
* `required` was made the default

In addition, a `required` option was also added to `has_one`.

These new qualifiers allow us to test these options appropriately.

Credit: Shia <rise.shia@gmail.com>
2017-10-06 23:38:47 -05:00
Elliot Winkler 5e1e531b8b Rails 5: Fix failing association matcher tests 2017-09-17 17:01:50 -05:00
Jacob Morris af85ac1f0e Add support for hbtm :join_table option
Rails supports declaring a `has_and_belongs_to_many` relationship with a
custom `:join_table` option and some developers want to assert that the
correct value is being used. Add `AssocationMatcher#join_table` to allow
developers to test the following:

1) That the :join_table option is being used for the relationship
2) That the *correct value* is being used
3) That the custom join table exists in the database
2015-04-04 00:19:28 -06:00
Jacob Morris fdde3a50a4 Explicitly test custom `:join_table` option #547
Some developers might want to pass a :join_table option to their HABTM
association declarations (e.g., `has_and_belongs_to_many :posts,
join_table: :users_and_their_posts). Those same developers, may want to
explicitly test the existence of the join table. Make this possible.
2015-04-03 23:33:19 -06:00
Sean Devine 948757b3f8 Dependent qualifier now supports more values
* :destroy
* :delete
* :nullify
* :restrict
* :restrict_with_exception (Rails 4+)
* :restrict_with_error (Rails 4+)
* true
* false
2015-01-20 14:10:14 -07:00
Elliot Winkler d86fe2d1c0 Fix test suite to properly tag example groups
When running unit tests we need to ensure that we are simulating how
other people will use the matchers as best we can. This means, for
instance, tagging any example groups that test controller matchers as
controller example groups.
2014-12-25 00:44:53 -05:00
Elliot Winkler 2162965445 Upgrade 4.1 appraisal to Rails 4.1.7
This version fixes a bug with habtm and a namespaced class name so that
when the association looks up that class it will look inside the model
namespace instead of the global namespace.
2014-11-06 14:16:54 -07:00
Elliot Winkler b4bf814b3b Fix association matchers + namespaced class_name
Fix `class_name` qualifier for association matchers so that if the
model being referenced is namespaced, the matcher will correctly resolve
the class before checking it against the association's `class_name`.

Take these models for instance:

module Models
  class Friend < ActiveRecord::Base
  end

  class User < ActiveRecord::Base
    has_many :friends, class_name: 'Friend'
  end
end

Here, the `has_many` is referring to Models::Friend, not just Friend.
Previously in order to test the association, you had to write:

    describe Models::User do
      it { should have_many(:friends).class_name('Models::Friend') }
    end

Now, `have_many` will attempt to resolve the string given to
`class_name` within the context of the namespace first before treating
it as a reference to a global constant. This means you can now write
this:

    describe Models::User do
      it { should have_many(:friends).class_name('Friend') }
    end
2014-11-06 14:15:00 -07:00
Elliot Winkler f922613386 Reorganize unit tests, part II
* Change 'spec' Rake task to 'spec:unit'
* Require unit_spec_helper.rb in unit tests, not spec_helper.rb
* Re-namespace files in spec/support/unit under UnitTests
* Files in spec/support/unit/helpers no longer automatically add
  themselves to RSpec - this happens in unit_spec_helper.rb
* Extract RecordWithDifferentErrorAttributeBuilder and
  RecordValidatingConfirmationBuilder to separate files
2014-11-05 09:53:20 -07:00
Elliot Winkler bbdf8a807e Reorganize unit tests, part I
* Move spec/shoulda to spec/unit_tests/shoulda
* Move spec/support/*.rb to spec/support/unit_tests/{helpers,matchers}
* Move spec_helper.rb to unit_spec_helper.rb
2014-11-04 14:43:59 -07:00