I'm setting up specs for my forms (using Reform gem) with RSpec and Shoulda Matchers gems. I'cant figure out why I'm having validation issues.
My actual configurations :
ruby '2.5.1'
gem 'rails', '~> 5.2.0'
gem 'rspec-rails', '~> 3.8'
gem 'shoulda-matchers', '~> 4.0'
gem 'reform-rails', '~> 0.1'
I've already tried different sorts of length validations in my form. But nothing make things much better.
Here is the minimal form used for testing :
# frozen_string_literal: true
class UserForm < Reform::Form
# == Properties ==========================================================
property :nickname
# == Validations ==========================================================
validates :nickname, presence: true, length: { minimum: 3, maximum: 100 }
end
Here is the spec :
# frozen_string_literal: true
RSpec.describe UserForm, type: :model do
subject { described_class.new(User.new) }
it { is_expected.to validate_presence_of :nickname }
it { is_expected.to validate_length_of(:nickname).is_at_least(3).is_at_most(100) }
end
Note that the validate_presence_of matcher works perfectly.
I'm having as RSpec output :
1) UserForm should validate that the length of :nickname is between 3 and 100
Failure/Error: it { is_expected.to validate_length_of(:nickname).is_at_least(3).is_at_most(100) }
Expected UserForm to validate that the length of :nickname is between
3 and 100, but this could not be proved.
After setting :nickname to ‹"xxx"›, the matcher expected the
UserForm to be valid, but it was invalid instead, producing these
validation errors:
* nickname: ["is too short (at least 3 characters)"]
I'm obviously excepting to make these sorts of validations working.
I hope I can find help here :)
Reform is known not to work with shoulda-matchers. Basically, all of the model matchers in shoulda-matchers work by:
Making a new instance of the class under test.
Setting attributes on that instance.
Calling validate on that instance.
Reading any possible validation errors from errors.
However, Reform doesn't work this way; you don't set properties on the form object individually and then call valid?, you call validate with the properties you want to set on the form object, and then those properties are validated in the process of being set. So that's likely why you're getting this error — because shoulda-matchers is trying to manually set the properties, and then validate is blowing them away.
Unfortunately, there's not a way to get shoulda-matchers to work with Reform without writing some kind of adapter. We don't plan on adding this to the gem any time soon but we will take a PR! Other than this, apparently the Reform team was talking about making some RSpec matchers in the spirit of shoulda-matchers, but I'm not sure how much progress has been made there, if any.
Sorry I couldn't be more helpful :/
Related
they fail with: unitialized constant ActiveModel::SecurePassword::InstanceMethodsOnActivation
I'm trying to update a rails 5.2 app to the rails 6 beta, and everything seems to be working fine except for my validate_presence_of specs.
For example
RSpec.describe Post, type: :model do
context 'validations' do
subject { Post.new(body: Faker::Movie.quote)}
it { should validate_presence_of :body }
it { should validate_length_of(:body).is_at_most(5000).on(:create) }
end
end
the validate_length_of passes fine, and the validate_presence of does not. Posts do belong to a user that inherits from a Clearance::User, but I'm not sure why the constant would vary on different tests for the same model?
I've made this work.
There's a Pull Request to shoulda_matchers here:
https://github.com/thoughtbot/shoulda-matchers/pull/1169
Until the shoulda_matchers team merge it, use my version here:
https://github.com/morsedigital/shoulda-matchers
In your Gemfile:
gem 'shoulda-matchers', groups: [:test], git: 'https://github.com/morsedigital/shoulda-matchers'
Turns out this is a bug with shoulda-matchers.
https://github.com/thoughtbot/shoulda-matchers/issues/1167
In my case this was quick fix:
# spec/rails_helper.rb - add to bottom
class ActiveModel::SecurePassword::InstanceMethodsOnActivation; end;
Testing Rails model validations with RSpec, without testing AR itself
Lets as setup we have model User:
class User < ActiveRecord::Base
validate :name, presence: true, uniqueness: { case_sensitive: false }, on: :create
validate :password, presence: true, format: { with: /\A[a-zA-z]*\z/ }
end
A see several ways to test this:
it { expect(user).to validate_presence_of(:name).on(:create) }
or
it do
user = User.create(name: '')
expect(user.errors[:name]).to be_present
end
My main question is which of the approaches is better and why? Can suggest me different approach?
Additional questions:
How much should I test? As an example, I can write so many tests for the regex, but it will be hell for maintenance.
How much you think will be full test coverage in this example?
The functionalities of:
Rails being able to validate the presence of an arbitrary value on your model
errors being added to an object for an attribute that is missing when a validation for it is configured
are covered in the tests for Rails itself (specifically, in the ActiveModel tests).
That leaves needing to write the tests for the config that covers the business logic of your app eg validating the presence of the specific name attribute on your specific User class etc. In my opinion, the matchers from the shoulda-matchers gem should have you covered:
RSpec.describe User, type: :model do
subject(:user) { build(:user) } # assuming you're using FactoryGirl
describe 'validations' do
specify 'for name' do
expect(user).to validate_presence_of(:name).on(:create)
# NOTE: saving here is needed to test uniqueness amongst users in
# the database
user.save
expect(user).to validate_uniqueness_of(:name)
end
specify 'for password' do
expect(user).to validate_presence_of(:password)
expect(user).to allow_value('abcd').for(:password)
expect(user).to_not allow_value('1234').for(:password)
end
end
end
I think that unless you have specific custom error messages for your errors that you want to test for (ie you've overridden the default Rails ones), then tests like expect(user.errors[:name]).to be_present can be removed (even if you have custom errors, I still think they're of dubious value since those messages will become locale-dependent if you internationalise your app, so I'd test for the display of some kind of error on the page in a feature spec instead).
I can write so many tests for the regex, but it will be hell for maintenance.
I don't think you can really get around this when testing validations for format, so I'd suggest just write some representative test cases and then add/remove those cases as you discover any issues you may have missed, for example:
# use a `let` or extract out into a test helper method
let(:valid_passwords) do
['abcd', 'ABCD', 'AbCd'] # etc etc
end
describe 'validations' do
specify 'for password' do
valid_passwords.each do |password|
expect(user).to allow_value(password).for(:password)
end
end
end
How much you think will be full test coverage in this example?
I've gotten 100% code coverage from reports like SimpleCov when writing unit specs as described above.
These 2 of them should be used, because:
it { expect(user).to validate_presence_of(:name).on(:create) }
=> You are expecting the validate_presence_of should be run on create, this should be the test case for model
it do
user = User.create(name: '')
expect(user.errors[:name]).to be_present
end
=> You are expecting a side effect when creating user with your input, so this should be the test case for controller
Why you shouldn't remove 1 of them:
Remove the 1st test case: what happens if you do database validation level instead, you expect an active record level validation
Remove the 2nd test case: what happens on controller actually creates a new User, how do you expect the error returning!
I'm practicing testing (just got into it), and am wondering whether it's better to use shoulda-matchers or Factory Girl - or a combination of both - in testing models. For example, I currently just use the simple shoulda-matchers test, which is nice and straightforward:
RSpec.describe User, :type => :model do
describe User do
it { should validate_presence_of(:username) }
it { should validate_presence_of(:password_decoded) }
it { should validate_length_of(:password).is_at_least(3) }
it { should validate_presence_of(:session_token) }
it { should have_one(:shopping_cart)}
end
end
But, from my understanding, this doesn't actually instantiate a User object like Factory Girl would. Is the above "sufficient" for testing? Any thoughts appreciated!
factory_girl and shoulda-matchers do two different things. Most Rails applications need both of those things. It's not either-or. The fact that the same people (Thoughtbot) are behind both gems is a good clue that they're both useful at the same time.
factory_girl allows you to create (only in memory or in the database) objects to use in your tests. Most applications need to create similar objects over and over again in their tests; factory_girl removes the duplication in doing that. It also allows you to customize a predefined object easily (more easily than a Rails fixture, for example). The model spec you showed doesn't need factory_girl, but if you have any code in your models more complicated than basic configuration it will probably be helpful to use factory_girl to create models to test.
shoulda-matchers makes it easier to assert that you got the results you expected in your tests. It provides RSpec matchers for asserting things about models and controllers. Most applications will find shoulda-matchers' ActiveModel matchers useful in ensuring that their models are well validated. (Personally I get more use out of shoulda-matchers' ActiveModel matchers than the ActiveRecord matchers.)
For basic associations and validations, I think shoulda matchers are fine. I use Factories to test other methods and more complex validations. Here's a simple example for a method that returns a value based on an instance's attributes. I've also shown how to use sequence to always generate a unique email address which can often trip you up in tests.
class User
def fullname
"#{firstname} #{surname}"
end
end
factories/users.rb
FactoryGirl.define do
sequence(:username) { |n| "user-#{n}" }
factory :user do
email { |_u| "#{FactoryGirl.generate(:username)}#example.com" }
password 'password'
password_confirmation 'password'
firstname 'John'
surname 'Smith'
end
end
user_spec.rb
RSpec.describe User do
describe "#fullname" do
it "returns the first and surnames separated by a space" do
user = FactoryGirl.create(:user)
expect(user.fullname).to eq "John Smith"
end
end
end
If you are going to have small models and tests you may create your example data using seeds but if you are going to add feature tests as well then I would suggest to use FactoryGirl.
I believe shoulda-matchers is a must for all tests.
I'm using the gem Frozen Record in order to setup a series of invariable questions in my Rails app, which will be accessed and used by other models. Given I'm used to Test Driven Development, I'd already set up a series of tests and validations before adding the Frozen Record gem:
class Question < FrozenRecord::Base
validates :next_question_id_yes, :question_text, :answer_type, presence: true
end
And the tests:
require 'rails_helper'
RSpec.describe Question, :type => :model do
let(:question) {Question.new(id: "1", question_text: "who's the daddy?", answer_type: 'Boolean', next_question_id_yes: '7', next_question_id_no: "9")}
it 'is valid' do
expect(question).to be_valid
end
#questions can be valid without a next_question_id_no, because some questions just capture info
#questions must have a next_question_id_yes as that will route them to the subsequent question
#if it's the last question in a block, we will use the next number up to signify the questions are complete
it 'is invalid without a next_question_id_yes' do
question.next_question_id_yes = nil
expect(question).to_not be_valid
end
it 'is invalid without a question_text' do
question.question_text = nil
expect(question).to_not be_valid
end
it 'is invalid without an answer_type' do
question.answer_type = nil
expect(question).to_not be_valid
end
end
All of these passed when I had the class Question < ActiveRecord::Base but since becoming a FrozenRecord I get:
rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/frozen_record-0.4.0/lib/frozen_record/base.rb:78:in `method_missing': undefined method `validates' for Question:Class (NoMethodError)
I presume there's no method in Frozen Record to validate the presence of these columns. What I'm less sure of is:
Do I need to perform validations on static data loaded via YAML? My first feeling was that this would be an additional check on my YAML code.
If I do, can I write custom validations to check this data?
Has anyone else used this gem and overcome the issue using Rspec? (it's been downloaded thousands of times according to RubyGems)
Any help would be appreciated.
Sorry to waste everyone's time - I just looked through the source code of FrozenRecord and worked out I can do it like this:
class Question < FrozenRecord::Base
include ActiveModel::Validations
validates :next_question_id_yes, :question_text, :answer_type, presence: true
self.base_path = '/config/initializers/questions.yml'
end
Should think before I ask next time.
How to test the following example?
class Post < ActiveRecord::Base
belongs_to :discussion, touch: true
end
You can set a message expectation:
it "should touch the discussion" do
post = Factory.build(:post)
post.discussion.should_receive(:touch)
post.save!
end
This examples uses Factory Girl, but you could use fixtures or mocks as well.
Firstly
If all you're trying to do is assert that the touch: true option is set on your association, then you can just do the following:
describe Post do
it { should belong_to(:discussion).touch(true) }
end
Secondly
For testing callbacks in general, read on.
All of the other answers here have two flaws:
They require a hit on the database, which can be slow.
They do not determine which callback is called during a save!
Instead, use the Shoulda Callback Matchers, which doesn't require a database hit and you can specify which callback you're testing the existence of.
Installation
Install the Shoulda Callback Matchers with Bundler:
group :test do
gem "shoulda-callback-matchers", "~> 1.0"
end
Usage
it { should callback(:some_method).after(:save) }
Thank you to Beat for writing this great library.
You can mock the #touch call, or verify the effects of your callback on it.
it "should touch the discussion" do
original_updated_at = #discussion.updated_at
#post.save!
#post.discussion.updated_at.should_not_be == original_updated_at
end