This is my first time testing in rails and I'm having trouble with what I think should be a pretty simple validation.
In group_spec.rb
it { should validate_presence_of(:enc_key) }
it { should validate_uniqueness_of(:enc_key).case_insensitive }
In my model
validates :enc_key, :presence => true, uniqueness: {:case_sensitive => false}
When I run rspec I'm getting
2) Group should require enc_key to be set
Failure/Error: it { should validate_presence_of(:enc_key) }
Expected errors to include "can't be blank" when enc_key is set to nil, got errors: ["owner There is no owner associated with this group. (nil)", "name can't be blank (nil)", "name is
too short (minimum is 4 characters) (nil)", "stripped_name can't be blank (nil)"]
The list of errors are generated from other validations and I've tried writing a custom validation but that didn't work either.
I'm guessing you are using shoulda matchers here and I can't see anything wrong with your test.
I think you should be using the validates method rather than the validate method in you model. validates is used to declare the kind of validation rules that you are specifying here. validate is used to declare custom validations.
So try this in your model:
validates :enc_key, presence: true, uniqueness: {case_sensitive: false}
See http://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html#method-i-validates
and http://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html#method-i-validate
The shoulda matchers are designed to work with otherwise valid records. They change various aspects of the record and test that the validation reports the appropriate error. If there are already errors in the record, this approach generally doesn't work.
You haven't shown the rest of your spec, but I gather than subject is nil. You need to set subject to be a valid instance of your record.
See https://github.com/thoughtbot/shoulda-matchers/issues/365 for a related discussion of this.
Related
I was looking for something similar to .net's data annotations in rails, something like this.
What I want to achieve by this is: I have some fields (they could be nil also) for which I want to check the length and if the length exceeds I want to display an error message.
I want to club all the error messages related to, say all blog posts (which again have many separate fields) and then display them at once.
Rails uses ActiveRecord validations. In many cases the default validations are easy to set up. But if you want/or need customized validations that can all be done as well. Read the documentation here:
http://guides.rubyonrails.org/active_record_validations.html
In your case this type of validation is built in to rails so it's simple as adding 1 line to your model:
class MyModel
validates :my_field_name, length: { maximum: 3 }, allow_blank: true
end
This will validate the maximum length of your field. You can also customize the validation error message:
class MyModel
validates :name, presence: {message: "Title can't be blank." }, uniqueness: {message: "Title already exists."}, length: { maximum: 5, message: "Must be less than 5 characters"}
end
When setting up my models I often find myself having to write out all of its attributes when setting up certain validations. A common example is when I use the presence parameter:
validates :first_name, :last_name, :username, :email, presence: true
Is there a clever way to select all of its attributes without explicitly writing them all out similar to how you can retrieve them in the rails console?
User.columns
And pass it as an argument in the validates method?
ALL_ATTRIBUTES = User.columns
validates ALL_ATTRIBUTES, presence: true
Trying something like this out I got this error undefined method 'to_sym'
I will NOT encourage you or anyone to do this. Reason being when you run into issues, when an object of your model doesn't get saved and throw errors because of a new column which was added to application after some time in future, and you or new developers will wonder WHY?!?!.
However, if you must do then here you go:
validates *self.column_names.map(&:to_sym), presence: true
Here, * in Ruby is known as splat operator and here's the explanation on &:.
This is an awful idea. But you can do it this way:
attrs = column_names.map { |column| column.to_sym }
validates *attrs, presence: true
Why is it a bad idea? Because it's not very clear what is being validated it. It makes debugging hard, and could cause you have strange bugs. If you add a column in the future that does not require presence validation, you will trip up. Also, some things my not require presence. For example, an email field will need a regex validation, which automatically knows that a blank string is invalid. So a presence validator is redundant.
Beware of being too clever, as it's sometimes not so clever after all.
I got a Devise model called Candidate.
And I got this test:
it { expect(subject).to validate_presence_of(:democracyengine_recipient_id) }
And this line in the model:
validates :democracyengine_recipient_id, presence: true
And the validation works (in other tests).
But in this particular test I get this error:
Failure/Error: it { expect(subject).to validate_presence_of(:democracyengine_recipient_id) }
Expected errors to include "can't be blank" when democracyengine_recipient_id is set to nil, got errors: ["email can't be blank (\"\")", "password can't be blank (nil)"]
How do I fix it?
presence: true is an ActiveRecord validator and in my opinion it should have been called as below : (NOTE: validates and not validate)
validates :democracyengine_recipient_id, presence: true
validate method adds a validation method or block to the class. This is useful when overriding the validate instance method becomes too unwieldy and you're looking for more descriptive declaration of your validations.
See more details in API Documentation for validate
For validates see official API Documentation for validates
I'm learning rails with the book Agile Web development with Rails 4e. It uses the following so far as our product model (adapted from a scaffold):
class Product < ActiveRecord::Base
attr_accessible :description, :image_url, :price, :title
validates :description, :title, :image_url, presence: true
validates :price, numericality: {greater_than_or_equal_to: 0.01}
validates :title, uniqueness: true
validates :image_url, allow_blank: true, format: {
with: %r{\.(gif|jpg|png)$}i,
message: 'Must be a valid URL for a gif, png, or jpg..'
}
end
I'm wondering why it tests first for the presence of :image_url, but then in the tertiary validation to make sure the image url is valid, it allows for blank responses which contradicts the first validation. I don't understand why this is supposed to work as is.
As an additional question, if the image_url is empty, how can I test if it is empty in my code? (e.g. in the product view to display a default image.)
Model validations are tested in isolation. A model is valid if and only if it passes validation for each validates statement independently.
It's probably bad-form, and evidently confusing for that allow_blank: true to be in the 4th validation, but that only applies to that single statement. The model must pass all validations to be considered valid, so the 1st statement merely imposes a tighter restriction than the 4th.
A final point, note that presence tests for non-nilness, whereas blank is defined as nil or the empty string. It is therefore possible to be both present and blank; e.g. image_url = ''. However, it remains the case that validations are tested separately in isolation.
I think maybe you are confused about the validation code? I'm a noob, and this is probably not entirely accurate: the validates keyword doesn't test for presence, it starts a block that you use to specify your validations.
As is, your code will validate the :image_url according to your specifications if it exists. If you took out allow_blank: true, then a nonexistent or blank :image_url would fail the validations.
I'm new to Rails as well and am using the same book. My understanding is that in order to stop the validation returning two errors immediately against validation (i.e. one if the field is blank and one if it doesn't have a correct file extension) it must allow_blank for the file format.
The best way I can explain it is to suggest removing the allow_blank: true code and trying to submit the description form again.
You should then see that you get both the validation errors for the field being blank and the file format being wrong.
The allow_blank therefore tells the validation to only error on the file format once the field is no longer blank.
Confused me as well which is why I ended up here!
If I have validations that require certain values in a field, like a format: { with: some_regex } or an inclusion: { in: some_array } that will fail on blank or nil, is setting the validation presence: true useful in any way? In the same situation, is the length: { } validation useful if the other validations would fail?
Similarly, could a length validation of {1..n} preclude the need for a presence validation?
I'm trying to learn if there is something more going on or if the checks are simply redundant. For instance, the Rails Tutorial has a presence: true validation on the email address but the format validation should not allow a blank or nil address. Seems redundant, or is it style/convention?
If you're using something like format: regex and you can guarantee (easy enough through a unit test) that a blank or nil attribute will cause the whole record to be invalid, then yes you don't need to validate the presence. Same with the inclusion situation, and the length situation.
In general, adding more validations isn't necessarily a bad thing. But, if you've thoroughly tested your model for situations with blank or nil attributes and your current validations achieve the desired result, then you're fine.
Validation of presence if weaker then anything mentioned. I'd rather not put
validates presence: true
having, for instance,
validates format: { with: some_regex }, allow_blank: false
as the later already would cover the situation with the field having nil value.