TDD in Rails: clarification on Red - Green testing - ruby-on-rails

Following Michael Hartl's Rails tutorial, I'm unsure how the following validation test is supposed to work:
test "name should be present" do
#user.name = " "
assert_not #user.valid?
end
When this test is written, the testing suite should be Red. After adding the corresponding part in the User class as such:
class User < ActiveRecord::Base
validates :name, presence: true
end
The test becomes Green. I don't understand how the former part works. Is the test Red because the validates part is not implemented yet? After implementing it, #user.valid? should be False turning into a True due to assert_not. Thus, the test is Green?

You have the correct understanding of it. Before a model in Rails is saved to the database, it must be validated. The test here is checking whether the user model would be considered valid and saved with a blank name. As such, before you add validates :name, presence: true, #user.valid? evaluates to true and the test fails. Once you add the validation, the model is considered invalid.

In a Test driven development (TDD) approach we write test before writing codes. Thus, we make the test fail first and then write some codes to make the test pass.

Looks like you do have a correct understanding of whats going on.
assert_not #user.valid? is saying that the #user object is invalid. To start with #user.name is set to a blank value; so we expect it the assertion to pass. However, the code is not yet checking for a valid name to be present. So it fails.
Adding validates :name, presence: true makes the model check for a valid name to be present. Thus the test passes.

Related

Testing uniqueness validation

I'm trying to learn rspec and have ran into a problem, I'm trying to test the uniqueness validation on one of my models, but the test keeps failing even though I'm pretty sure it should pass.
Here is my test:
context "two products with the same title" do
Given{FactoryGirl.build(:product, title: "Hello test title")}
Given(:post2){FactoryGirl.build(:product, title: "Hello test title")}
Then{post2.invalid?}
end
and here is my validator:
validates :title, uniqueness: true
however when I run the test it comes back failed and I'm not sure why?
any help would be great!
You need to add uniqueness validation on title:
validates :title, uniqueness: true
And also you have to create first product not just build it
context "two products with the same title" do
Given{FactoryGirl.create(:product, title: "Hello test title")}
Given(:post2){FactoryGirl.build(:product, title: "Hello test title")}
Then{post2.invalid?}
end
This will create one product with title = "Hello test title"
And for second product with same title product will be invalid
You should use a gem like shoulda-matchers for testing this kind of tests: https://github.com/thoughtbot/shoulda-matchers
It will save you a lot of time and it will DRY your tests (since they are all the same)
Regarding your test, I'm not sure what are you trying to achieve. You are not validating uniqueness there, only length of the product. For adding the uniqueness you add to your Product model:
validates :title, uniqueness: true
And when making the tests, you should create (instead of build) your first Product. Basically, unless your Product is stored in the database, your products will be valid because it doesn't exists (yet) any other Product like that one.

How does this length validation work in Ruby on Rails?

I am following the Rails tutorial by Michael Hartl. In chapter 6 we're creating a length validation test for a users name and email.
In the test/models/user_test.rb file he says to write
test "name should not be too long" do
#user.name = "a" * 51
assert_not #user.valid?
end
and then in app/models/user.rb we put
class User < ActiveRecord::Base
validates :name, presence: true, length: { maximum: 50 }
My question is, how does the test ensure that the name is not, for example, 60 characters long? I get that the validation says to make the max length 50, but the test says assert that the user is not valid if user.name EQUALS 51 characters...not greater than or equal to.
To be completely honest, I don't understand the relationship between why you need the validates in user.rb and then also the test file, so that could be why I'm confused.
In tests, you need to ensure that your code does everything as you expect. In this case that you can't save the user with name which is longer than 50 symbols. Thats why in test you are checking that user becomes invalid with name length == 51 symbol.
But you are also correct that this test doesn't guarantee that user with name length 60 will be invalid too. It also doesn't check that 50 is the maximum, because it will pass for
validates :name, presence: true, length: { maximum: 1 }
for example. But you probably don't want your app to behave like this. That's why I also encourage you to add another check for maximum allowed length:
#user.name = "a" * 50
assert #user.valid?
Usually, if you are doubt that something can be wrong in your code, you are free to add new tests. But in this case, you shouldn't actually test how the code behaves, because presence/length validations are well-tested in Rails itself. You should just check that you included such validations in your model with correct arguments passed. For example, in shoulda-matchers you have these helpers:
should validate_presence_of(:name)
should validate_length_of(:name).is_at_most(50)
I am not sure if unit-test have the analogs, most likely no, so you should test it yourself in the way you do this and assume this is enough.
The test is just asserting that a user name with 51 characters should not be a valid one.
The test doesn't 'ensure' that the user name can't be 60 characters long. That's what the actual validation code does.
For example, if you were to change the validation code to this:
class User < ActiveRecord::Base
validates :name, presence: true, length: { maximum: 60 }
then the test would fail because the code is validating a user name with 51 characters.

Validating presence with rspec

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.

testing field type a model in mongoid

i use ruby on rails. also i use minitest framework for testing and mongoid for database. i want to write a model test. my model is below:
class Identity
include Mongoid::Document
include OmniAuth::Identity::Models::Mongoid
field :name
field :email
field :password_digest
validates :name, uniqueness: true
end
the model test is:
describe Identity do
it "must include OmniAuth::Identity::Models::Mongoid" do
Identity.must_include OmniAuth::Identity::Models::Mongoid
end
it "should have name" do
Identity.new.must_respond_to :name
end
it "should have email" do
Identity.new.must_respond_to :email
end
it "should have password_digest" do
Identity.new.must_respond_to :password_digest
end
it "should type of String" do
Identity.new.name.type.must_equal "String"
end
end
my problem is about testing the field's type
it "should type of String" do
Identity.new.name.type.must_equal "String"
end
How can i test a field's type? Thanks in advance.
Just a tip - while not an answer to your actual question, it is usually not that
wise test implementation details of code, because it is more likely
to change, and if you think of system validation through tests, it is not that important
how it is implemented, but what it does, eg. how it behaves.
A canonical example is that of testing functionality of a Stack class. Instead of
pushing and popping items out of stack and checking the size, it is likely better to
just push and pop things, and see that if you pop an empty stack, you get appropriate
exception. And naturally you want to check that items are returned in last in, first out
(LIFO) order.
So, in your case, instead of testing what kind of type your field name is, rather
test what you do with the name.

What "validation logic" should I test?

I am using Ruby on Rails 3.0.9 and RSpec 2. I would like to know what "validation logic" I should test. That is, if in my model I have:
class User < ActiveRecord::Base
validates :firstname
:presence => true
end
What should I test of the following?
"should have a valid first name"
"should not have a valid first name"
Or should I test both?
You can test both by simply doing this:
it "should validate presence of" do
should validate_presence_of :firstname
end
Take a look at the shoulda matchers for all such standard Rails Validation.
I think you should not test both. This will be enough:
describe User do
it { should validate_presence_of(:firstname) }
end
There is basically no algorithm for validating names, because the form of names is incredibly culture-centric. So, really you should avoid complex validations for something like a person's name. Some places/cultures don't have last names, for example, so even validating their presence isn't proper. There's a whole list of other examples that make validating names a really bad idea. For more information on the issue of validating names itself, see my answer to this question.
That being said, in general, when validating any field, I test both valid and invalid data. I make sure that, when I set a field to a valid value, that the .valid? method returns true, and when it's invalid, that it returns false.
Typically you don't need to do a long list, you just need to test
A typical valid and invalid example
A few edge cases
you can also test for specific values:
describe User do
context "validations" do
it { should_not allow_value("admin").for(:firstname) }
it { should allow_value("JohnDoe").for(:firstname) }
end
end

Resources