There is the following spec:
require 'spec_helper'
describe Place do
let(:place) { FactoryGirl.create(:place) }
subject { place }
it { expect be_valid }
describe 'when content is not present' do
before { place.content = nil }
it { expect be_valid }
end
end
Also there is validation for presence content in Place model. But this spec doesn't throw any exception, even if the last instruction sets 'content' as nil. What's the trouble? Thanks.
You are just setting an expectation but never calling anything.
Replace your expect be_valid calls with
it { expect(subject).to be_valid }
...
it { expect(place).to be_valid }
I guess you are coming from the old should syntax. There you could have written it the way you did:
it { should be_valid }
But the newer expect syntax behaves slightly different
You have let(:place) { FactoryGirl.create(:place) }, so when you reference place for the first time then it creates an instance (with valid content). You are then changing content, but validation is not performed on it.
There are a few ways for doing what you need:
Use the factory that you already have, but instead of create use build
Define an invalid factory at FactoryGirl:
factory :place do
content 'valid value'
factory :invalid_place do
content nil
end
end
3) Or you can use attributes_for
Related
I'm trying to write terse tests for an API controller, but I'm having trouble with the "one-liner" syntax offered by RSpec.
I'm overriding the subject explictly to refer to the action of posting rather than the controller:
let (:params) { some_valid_params_here }
subject { post :create, params }
When I use the one-liner syntax to test http_status, it works fine:
it { is_expected.to have_http_status(:created) }
# pass!
But when I try to use it for a different expectation, it blows up:
it { is_expected.to change{SomeActiveRecordModel.count}.by(1) }
# fail! "expected result to have changed by 1, but was not given a block"
Notably, when I run this second expectation in a longer form, calling on subject explictly, it works:
it "creates a model" do
expect{ subject }.to change{SomeActiveRecordModel.count}.by(1)
end
# pass
Is this just a weakness of the one-liner syntax, that it can't handle this more complicated expression? Or have I misunderstood something about how subject is inferred into these tests?
(NB: I know that setting the subject to an action has some detractors, and I'm happy to hear opinions, but that isn't the aim of this question).
You can do it like this
subject { -> { post :create, params } }
and then
it { is_expected.to change(SomeActiveRecordModel, :count).by(1) }
Here you have very nice discussion about this
github_topic
As it was said before there's a simple solution involving subject ; it does not require a lambda tho ; I applied it this way for my CategoryController#show :
describe '#show' do
subject { get :show, id: category }
context "as guest" do
it { is_expected.to render_template('show') }
end
end
I'm refactoring my model rspecs as to be "as DRY" as possible, leading to something like
require 'spec_helper'
describe Model do
subject { build(:model) }
it { should be_valid }
it { should validate_presence_of(:description) }
it { should ensure_length_of(:description).is_at_least(3).is_at_most(255) }
it { should validate_presence_of(:position) }
it { should validate_numericality_of(:position).is_greater_than_or_equal_to(1) }
end
Now, every file starts with
subject { build(:model) }
it { should be_valid }
so, you guess it, I would like to get rid of these two lines as well...
Any suggestions?
The it { should be_valid } test seems to be testing only your factory. It's not really important to the function of the Model. Consider moving these tests to a single factories_spec test if you'd like to test them. See: https://github.com/thoughtbot/suspenders/blob/master/templates/factories_spec.rb
The matchers you are using in your example don't really require a model built with FactoryGirl. They will work fine with the implicit, default subject (Model.new). When that's not the case, I'd suggest defining as much of the state of your test as possible inside the test -- that is, inside the it blocks. If that results in some duplication, so be it. Particularly costly duplication can be extracted to method calls, which are preferable to subject, let and before because there's no magic to them. As a developer coming back to the project in 6 months, looking at spec on line 75, you'll know exactly what the setup is.
See: http://robots.thoughtbot.com/lets-not
You can use rspec shared examples:
shared_examples "a model" do
subject { build described_class }
it { should be_valid }
end
describe Foo do
it_behaves_like "a model"
end
describe Bar do
it_behaves_like "a model"
end
This should be simple but I cannot figure out why it doesn't work. I have seen many more complex uniqueness constructs here. I have column that should be a unique index. I have specified it twice in the model, just testing options, but my test for uniqueness continues to fail.
Model validation code:
validates :feed_url, presence:true,
format: {with: VALID_URL_REGEX},
uniqueness: true
validates_uniqueness_of :feed_url
RSpec code:
before do
#feed = FactoryGirl.create(:feed)
end
...
describe "when URL is already taken" do
before do
feed_with_same_url = #feed.dup
feed_with_same_url.feed_url = #feed.feed_url
feed_with_same_url.save
end
it { should_not be_valid }
end
The save should not be valid but the tests fails because the model says it is valid even though the fields are equal. I have checked the fields myself at a breakpoint and they are exactly the same in case, length, value, etc.
Tests for presence and Regex validity work perfectly, so the model is working.
I am trying to do this in the model as opposed to the index. I believe I read last night that Rails 4 prefers (deprecates?) these tests in the code instead of the database, but I cannot find that source tonight. (?) Either way, I'd like to see the model working.
You have to call should_not be_valid on some object. Try this
before(:each) do
#feed_with_same_url = #feed.dup
#feed_with_same_url.feed_url = #feed.feed_url
end
it { #feed_with_same_url.should_not be_valid }
What is it in the context of that test? Without the full code, it's probably not feed_with_same_url, so your test is not checking what you think it's checking.
If I was writing this test, it would be something like:
let(:feed) { FactoryGirl.create :feed }
let(:feed_with_same_url) { feed.dup }
subject { feed_with_same_url }
it { should_not be_valid }
Now it is the subject, which is feed_with_same_url.
After learning about shoulda-matchers by answering another StackOverflow question on attribute accessibility tests (and thinking they were pretty awesome), I decided to try refactoring the model tests I did in The Rails Tutorial in an attempt to make them even more concise and thorough. I did this thanks to some inspiration from the documentation for modules Shoulda::Matchers::ActiveRecord and Shoulda::Matchers::ActiveModel, as well as this StackOverflow answer on structuring shoulda tests in models. However, there's still a few things I am not sure about, and I am wondering how these tests could be made better.
I will use the User spec in the Rails Tutorial as my example as it is the most detailed, and covers lots of areas that could be improved. The following code example has been changed from the original user_spec.rb, and replaces the code down until the describe "micropost associations" line. The spec tests against the user.rb model, and its factory is defined in factories.rb.
spec/models/user_spec.rb
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# name :string(255)
# email :string(255)
# created_at :datetime not null
# updated_at :datetime not null
# password_digest :string(255)
# remember_token :string(255)
# admin :boolean default(FALSE)
#
# Indexes
#
# index_users_on_email (email) UNIQUE
# index_users_on_remember_token (remember_token)
#
require 'spec_helper'
describe User do
let(:user) { FactoryGirl.create(:user) }
subject { user }
describe "database schema" do
it { should have_db_column(:id).of_type(:integer)
.with_options(null: false) }
it { should have_db_column(:name).of_type(:string) }
it { should have_db_column(:email).of_type(:string) }
it { should have_db_column(:created_at).of_type(:datetime)
.with_options(null: false) }
it { should have_db_column(:updated_at).of_type(:datetime)
.with_options(null: false) }
it { should have_db_column(:password_digest).of_type(:string) }
it { should have_db_column(:remember_token).of_type(:string) }
it { should have_db_column(:admin).of_type(:boolean)
.with_options(default: false) }
it { should have_db_index(:email).unique(true) }
it { should have_db_index(:remember_token) }
end
describe "associations" do
it { should have_many(:microposts).dependent(:destroy) }
it { should have_many(:relationships).dependent(:destroy) }
it { should have_many(:followed_users).through(:relationships) }
it { should have_many(:reverse_relationships).class_name("Relationship")
.dependent(:destroy) }
it { should have_many(:followers).through(:reverse_relationships) }
end
describe "model attributes" do
it { should respond_to(:name) }
it { should respond_to(:email) }
it { should respond_to(:password_digest) }
it { should respond_to(:remember_token) }
it { should respond_to(:admin) }
it { should respond_to(:microposts) }
it { should respond_to(:relationships) }
it { should respond_to(:followed_users) }
it { should respond_to(:reverse_relationships) }
it { should respond_to(:followers) }
end
describe "virtual attributes and methods from has_secure_password" do
it { should respond_to(:password) }
it { should respond_to(:password_confirmation) }
it { should respond_to(:authenticate) }
end
describe "accessible attributes" do
it { should_not allow_mass_assignment_of(:password_digest) }
it { should_not allow_mass_assignment_of(:remember_token) }
it { should_not allow_mass_assignment_of(:admin) }
end
describe "instance methods" do
it { should respond_to(:feed) }
it { should respond_to(:following?) }
it { should respond_to(:follow!) }
it { should respond_to(:unfollow!) }
end
describe "initial state" do
it { should be_valid }
it { should_not be_admin }
its(:remember_token) { should_not be_blank }
its(:email) { should_not =~ /\p{Upper}/ }
end
describe "validations" do
context "for name" do
it { should validate_presence_of(:name) }
it { should_not allow_value(" ").for(:name) }
it { should ensure_length_of(:name).is_at_most(50) }
end
context "for email" do
it { should validate_presence_of(:email) }
it { should_not allow_value(" ").for(:email) }
it { should validate_uniqueness_of(:email).case_insensitive }
context "when email format is invalid" do
addresses = %w[user#foo,com user_at_foo.org example.user#foo.]
addresses.each do |invalid_address|
it { should_not allow_value(invalid_address).for(:email) }
end
end
context "when email format is valid" do
addresses = %w[user#foo.COM A_US-ER#f.b.org frst.lst#foo.jp a+b#baz.cn]
addresses.each do |valid_address|
it { should allow_value(valid_address).for(:email) }
end
end
end
context "for password" do
it { should ensure_length_of(:password).is_at_least(6) }
it { should_not allow_value(" ").for(:password) }
context "when password doesn't match confirmation" do
it { should_not allow_value("mismatch").for(:password) }
end
end
context "for password_confirmation" do
it { should validate_presence_of(:password_confirmation) }
end
end
# ...
end
Some specific questions about these tests:
Is it worth testing the database schema at all? A comment in the StackOverflow answer mentioned above says "I only test things that are related to behavior and I don't consider the presence of a column or an index behavior. Database columns don't just disappear unless someone intentionally removes them, but you can protect against that with code reviews and trust", which I agree with, but is there any valid reason why the structure of the database schema would be tested for, and thus justifying the existence of the Shoulda::Matchers::ActiveRecord module? Perhaps just the important indexes are worth testing...?
Do the should have_many tests under "associations" replace their corresponding should respond_to tests under "model attributes"? I can't tell whether the should have_many test just looks for the relevant has_many declaration in a model file or actually performs the same function as should respond_to.
Do you have any other comments/suggestions to make these tests more concise/readable/thorough, both in content and structure?
1) The Shoulda::Matchers::ActiveRecord module has a lot more in it than just column and index matchers. I would dig around in the included classes a little and see what you can find. This is where the have_many, belong_to etc come from. For the record though, I see little value in most of what is in there.
2) Yes, macros such as have_many test a lot more than whether or not a model responds to a method. From the source code, you can see exactly what it is testing:
def matches?(subject)
#subject = subject
association_exists? &&
macro_correct? &&
foreign_key_exists? &&
through_association_valid? &&
dependent_correct? &&
class_name_correct? &&
order_correct? &&
conditions_correct? &&
join_table_exists? &&
validate_correct?
end
3) Making the tests more readable and/or concise is definitely a subjective question to answer. Everyone will give you a different answer to this depending on their background and experience. I would personally get rid of all of the respond_to tests and replace them with tests that have value. When someone looks at your tests, they should be able to understand the public API for that class. When I see that your objects respond_to something like "following?", I can make assumptions, but don't really know what it means. Does it take an argument? Does it return a boolean value? Is the object following something or is something following the object?
Your question touched on a few points, I would like to address two of them:
The answer is subjective, so I will give you my personal take.
1) Test ActiveRecord that way?
My answer is yes. You could write complex tests with real data but if you basically trust ActiveRecord you can do it this way and if you get to doing tdd, with these tests first they can help in that process.
2) Write the model tests at all?
My answer is yes. What I do is focus controller and request specs on the happy path and then for cases where validations and the like are needed I write unit model tests for them. This has turned out to be a good division of responsibility to me.
I think this whole thing should be seen from specification point of view.
If you have a component-testing-level specification which covers the necessary database columns for the given model, you should, otherwise not.
If not covered, but as a responsible developer you feel it important to have (your sw and its quality characteristics are better that way), you have to arrange to include this info in the spec, then you can put these tests in the test suite.
Lower testing levels' requirements mostly come from within your organization (internal docs), customer mostly provides only the customer requirement spec (let's say this is the highest level in testing V-model).
As your organization starts design the sw creates the lower test levels specs step by step.
For the "do we really need this" question: it depends on many things: the app complexity, safety critical or not, standards to follow, contractual/legal/industrial regulations, etc.
In generally I would say, for a correct ideal application reqirement responsible for unit testing should write the unit level spec and tester should implement test based on this spec.
For "have_many and respond_to" I am afraid I have no background info how they are implemented, so can not answer.
I've found some value in writing tests for the presence of database columns. Here's why:
1) Writing them keeps me in the rhythm of TDD.
2) Migrations are usually pretty awesome, until they aren't. I know you're not supposed to edit an existing migration, but when I'm just working on something myself I sometimes do it anyway. And if someone else is working on the same application and changes an existing migration instead of writing a new one, these tests have isolated the problem pretty quickly for me.
If you get bogged down with too many column names & types you can do something like this to save yourself typing:
describe User do
describe 'database' do
describe 'columns' do
%w[reset_password_sent_at remember_created_at current_sign_in_at
last_sign_in_at confirmed_at confirmation_sent_at
created_at updated_at
].each do |column|
it { should have_db_column(column.to_sym).of_type(:datetime) }
end
end
describe 'indexes' do
%w[confirmation_token email reset_password_token
].each do |index|
it { should have_db_index(index.to_sym).unique(true)}
end
end
end
end
Hope that helps.
I have a custom validator (located in app/validators/uri_validator.rb) which is used in:
validates :link, uri: true
How do I specify this in my specs?
Ideally I would like to have a one-line call, such as:
it { should validate_uri_of(:link) }
How do I do this?
Another option is to use the allow_value matcher, although not ideal it can work in some circumstances.
it { should allow_value(value_which_is_valid).for(:link) }
it { should_not allow_value(value_which_is_invalid).for(:link) }
Use factory girl to build a model object with invalid data and one with valid data and call the be_valid matcher.
#factory girl
build(:model, link: valid_uri).should be_valid
build(:model, link: invalid_uri).should_not be_valid