I'm working on a goal application, where a user can check a box next to a goal to mark it as complete. I wrote the test for this functionality, but something isn't quite right, because it keeps failing. I eventually got fed up and just wrote the code, which works, but the test still keeps failing. I'm still learning Rspec, so any advice you could give me would be helpful.
The Test
# #user has already been signed in
describe "completing a goal" do
before(:each) do
#goal = Factory(:goal, :user => #user)
end
it "should set 'completed' attribute to true" do
visit edit_goal_path(#goal)
check('goal_completed')
click_button('goal_submit')
#goal.completed.should be_true
end
end
The Result
Failures:
1) Goals completing a goal should set 'completed' attribute to true
Failure/Error: #goal.completed.should be_true
expected nil to be true
# ./spec/requests/goals_spec.rb:81:in `block (3 levels) in <top (required)>'
If you want this to be an integration test, it would be better for your assertion to be about the contents of the page. Something like:
assert_text "Goal met on 1/1/2011"
If you want to stick with model assertions, I'm pretty sure you just need to reload:
#goal.reload.completed.should be_true
Related
I've looked through a few posts with the same issue, but still feel like mine is a bit different.
viewing_categories_spec.rb
require 'rails_helper'
RSpec.feature 'Users can view categories' do
scenario 'with the category details' do
category = FactoryBot.create(:category, name: 'Real Estate')
visit '/categories'
click_link('Real Estate')
expect(page.current_url).to eq category_url(category)
end
end
category_factory.rb
FactoryBot.define do
factory :category do
name {"Computers"}
end
end
when I run rspec, I'm getting an error:
Failures:
1) Users can view categories with the category details
Failure/Error: click_link('Real Estate')
Capybara::Ambiguous:
Ambiguous match, found 2 elements matching visible link "Real Estate"
# ./spec/features/viewing_categories_spec.rb:8:in `block (2 levels) in <main>'
Then I've modified spec by adding match: :first:
require 'rails_helper'
RSpec.feature 'Users can view categories' do
scenario 'with the category details' do
category = FactoryBot.create(:category, name: 'Real Estate')
visit '/categories'
click_link('Real Estate', match: :first)
expect(page.current_url).to eq category_url(category)
end
end
This time I got error:
Failures:
1) Users can view categories with the category details
Failure/Error: expect(page.current_url).to eq category_url(category)
expected: "http://www.example.com/categories/265"
got: "http://www.example.com/categories/17"
(compared using ==)
# ./spec/features/viewing_categories_spec.rb:9:in `block (2 levels) in <main>'
I noticed that sometimes, I'm not seeing the error and sometimes it shown up.
The only thing I see always is "http://www.example.com/categories/17".
This part remains same always when I run rspec command.
The full source code is here https://github.com/tenzan/kaganat
The fact that the "http://www.example.com/categories/17" url is constant and that Capybara is seeing two "Real Estate" links on the page when your test appears to only create one leads me to believe that you have some old data left in your test database. By opting to use match: :first you've just covered up the fact that you have more records existing than you expect and that error should have been your first clue (along with just looking at a screenshot of the test running). Something like
rails db:reset RAILS_ENV=test
will clear out your test database and ensure you don't have old data hanging around. You'll also want to go back to your original click_link('Real Estate') without the :match setting. Additionally, if you want stable tests, you should almost never by using the standard RSpec matchers ('eq', etc) with Capybara returned objects since page load/behavior is an asynchronous thing. Instead you should should the matchers provided by Capybara. In your current example that means instead of writing expect(page.current_url).to eq category_url(category) you should be writing expect(page).to have_current_path(category_url(category))
I have been working through the Hartl sample app course, and while I have had issues here and there, I have always been able to find answers, or identify the kink in my code by comparing with the tutorial git. However this time I am at a loss and am hoping someone in the community can figure out where I may have gone wrong.
I am getting the following minitest error:
Minitest::Assertion: Expected at least 1 element matching "a[href="/users/14035331"]", found 0..
Expected 0 to be >= 1.
test/integration/users_index_test.rb:15:in `block (2 levels) in <class:UsersIndexTest>'
test/integration/users_index_test.rb:14:in `block in <class:UsersIndexTest>'
Here is the code for the test which is exactly as it appears in the tutorial:
require 'test_helper'
class UsersIndexTest < ActionDispatch::IntegrationTest
def setup
#user = users(:james)
end
test "index including pagination" do
log_in_as(#user)
get users_path
assert_template 'users/index'
assert_select 'div.pagination'
User.paginate(page: 1).each do |user|
assert_select 'a[href=?]', user_path(user), text: user.name
end
end
end
User 14035331 is the ID of the first record in the test.db. Possible issues I can think of but haven't been successful in identifying - User is not on Page 1 (this shouldn't be since it is the first ID record); the test is looking for the NAME field but getting the ID field.
I did find a similar question on here, but it doesn't address this problem. You can see the thread here: Got failure in integration testing of rails
That user is a bit further along, and I really hate to refractor and push forward when my test suite isn't passing.
Thanks to HASHROCKET I was able to determine that somehow my DB was sorted by ID DESC and my test expected it to be ASC. I am not sure how that happened exactly but I added the following line to the end of users.yml to force order by ID ASC and the test is green.
<% User.order(id: :asc) %>
TL;DR: How can I stop rspec aborting all specs when it encounters an error?
When running rails' built in testing suite, I get output similar to the following:
...F..F.EE...E
Finished in 0.64396 seconds
14 examples, 2 failures, 3 errors
The . represents a passing test, the F a failing test and the E an erroneous test. A failing test means the code of your rails application is failing, and an erroneous test means the code of your actual test is failing. Erroneous tests are never good.
What I do like about this though, is that when the program encounters an E, it keeps on going through all the other tests.
I'm using rpsec, and while I do like it, I sort of hate how, when it encounters an erroneous spec, it completely gives up the ghost, and quits all specs, like this:
12:39:37 - INFO - Running: spec
/usr/local/rvm/rubies/ruby-1.9.3-p392/lib/ruby/gems/1.9.1/gems/activerecord-4.0.1/lib/active_record/validations.rb:57:in `save!': Validation failed: Email has already been taken (ActiveRecord::RecordInvalid)
from /usr/local/rvm/rubies/ruby-1.9.3-p392/lib/ruby/gems/1.9.1/gems/activerecord-4.0.1/lib/active_record/attribute_methods/dirty.rb:41:in `save!'
from /usr/local/rvm/rubies/ruby-1.9.3-p392/lib/ruby/gems/1.9.1/gems/activerecord-4.0.1/lib/active_record/transactions.rb:275:in `block in save!'
from /usr/local/rvm/rubies/ruby-1.9.3-p392/lib/ruby/gems/1.9.1/gems/activerecord-4.0.1/lib/active_record/transactions.rb:326:in `block in with_transaction_returning_status'
While I do like the error reporting, I really want it to carry on running all specs when it encounters an erroneous spec. Rather than the above output, I really want something along the lines of the following:
12:40:01 - INFO - Running: spec
...F..F..E
Failures:
1) User A new user can be created email should == "awesomedog#hotmail.co.uk"
Failure/Error: its(:email) { should == "awesomedog#hotmail.co.uk" }
expected: "awesomedog#hotmail.co.uk"
got: "awesomedozg#hotmail.co.uk" (using ==)
# ./spec/model/user_spec.rb:11:in `block (3 levels) in <top (required)>'
2) User A new user can be created should not be valid
Failure/Error: it { should_not be_valid }
expected valid? to return false, got true
# ./spec/model/user_spec.rb:20:in `block (3 levels) in <top (required)>'
Errors:
1) `save!': Validation failed: Email has already been taken (ActiveRecord::RecordInvalid)
Finished in 0.64396 seconds
10 examples, 2 failures, 1 error
Failed examples:
rspec ./spec/model/user_spec.rb:11 # User A new user can be created email should == "awesomedog#hotmail.co.uk"
rspec ./spec/model/user_spec.rb:20 # User A new user can be created should not be valid
I'm using factory-girl and factories in my specs, and data-base cleaner to clean my database between each spec. I'm using rspec-guard to run all specs on the event of any project file (apart from those in tmp,log or db) being saved. Because rpsec keeps wimping out if it hits an error, I'm getting this error:
How can I clean my database between erroneous rspec specs?
Basically, database-cleaner is configured to clean my database when a spec begins and when a spec finishes. Because rspec quits in the middle of a spec when it hits an error, database-cleaner doesn't detect that the spec has finished, and so never cleans my database. This means I have to manually empty it with my database shell.
I'd also prefer to see the state of all my other specs, even if one is erroneous! Rspec is really silly in this regard methinks!
Here are my factories and my spec:
spec/model/user_spec.rb:
require 'spec_helper'
describe User do
context "Valid user" do
user = FactoryGirl.create(:user_with_all_valid)
subject { user }
its(:first_name) { should == "Jimmy" }
its(:last_name) { should == "Thehat" }
its(:profile_name) { should == "Jimbohatboy893" }
its(:email) { should == "awesomedog#hotmail.co.uk" }
its(:password) { should == "thisisasupersecretpassword12234234" }
its(:password_confirmation) { should == "thisisasupersecretpassword12234234" }
end
end
spec/factories.rb:
FactoryGirl.define do
factory :user_with_all_valid, class: User do
first_name "Jimmy"
last_name "Thehat"
profile_name "Jimbohatboy893"
email "awesomedog#hotmail.co.uk"
password "thisisasupersecretpassword12234234"
password_confirmation "thisisasupersecretpassword12234234"
end
end
Your issue is this line:
context "Valid user" do
user = FactoryGirl.create(:user_with_all_valid) # Right here!
...
Test setup should always be done in a before block; variable definition is done in a let block. Doing it in your test definition will cause rspec to fail as described. In general, you should only ever have rspec directives (before, after, let, subject, it, its, etc) in a context block. If you're running test setup, application code, whatever, you're going to open yourself up to this kind of problem. What you want is:
context "Valid user" do
let(:user) { FactoryGirl.create(:user_with_all_valid) }
or:
context "Valid user" do
before { #user = FactoryGirl.create(:user_with_all_valid) }
The suite will then run the whole thing as expected, and report failures for the tests that fail to do their defined setup (the user definition) properly, rather than bombing out of the whole suite.
You need to have a unique email each time due to the validation on the model.
btw the other 7 or 8 are passing (dots).
Try adding:
new_user_time = Time.new
to save the current time.
Then pass that in to the create user factory.
Then use it to check that the users email is correct.
Use sequences as detailed at: https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#sequences
Unique values in a specific format (for example, e-mail addresses) can be generated using sequences. Sequences are defined by calling sequence in a definition block, and values in a sequence are generated by calling FactoryGirl.generate:
# Defines a new sequence
FactoryGirl.define do
sequence :email do |n|
"person#{n}#example.com"
end
end
FactoryGirl.generate :email
# => "person1#example.com"
FactoryGirl.generate :email
# => "person2#example.com"
...
The just have your spec check that part of the email address is correct, e.g. the #something.com or use a regular expression to compare, e.g. match(/person.*#something\.com/)
I'm testing some methods that involve email, and am trying to use a mock mailer object. I'm obviously going about it the wrong way, because the test works the first time but the very same test fails subsequently. I'd appreciate it if anyone can explain what is going on here. Thanks.
describe SentMessage do
before(:each) do
Notifier ||= mock('Notifier', :send_generic => true)
end
it "Sends an email" do
Notifier.should_receive(:send_generic).with(['a#contact.com'], 'test')
Notifier.send_generic(['a#contact.com'], 'test').should_not be_nil
end
it "Sends an email" do
Notifier.should_receive(:send_generic).with(['a#contact.com'], 'test')
Notifier.send_generic(['a#contact.com'], 'test').should_not be_nil
end
end
Result:
Failures:
1) SentMessage Sends an email
Failure/Error: Notifier.send_generic(['a#contact.com'], 'test').should_not be_nil
expected: not nil
got: nil
# ./spec/models/test.rb:14:in `block (2 levels) in <top (required)>'
Rspec inserts setup/teardown stuff for mocks and expectations. This does stuff like verify that should_receive expectations have been met and clear up mocks set in objects that endure beyond a single spec. For example if you had stubbed User.find in one spec, you wouldn't expect that stub to exist in another spec.
So at the end of the first spec, rspec is removing the stub setup in the before each. Because you're doing ||=, Notifier isn't recreated and neither is the stub. This in turn means that when you call should_receive in the second spec you're setting up a new stub. Since this new stub has no specified return value, nil is returned.
I was just starting out with RSpec, and im trying to do something quite trivial, but I couldnt find any good documentation on a good way of doing this. I want to test a sequence of events, say the friendship between two users.
My spec is:
describe User do
describe "friendships" do
describe ".friends?" do
before do
#user1 = FactoryGirl.build(:user)
#user2 = FactoryGirl.build(:user)
end
it "should be false for non friends" do
#user1.friends?(#user2).should be false
#user2.friends?(#user1).should be false
end
it "should be false for requested friendship" do
Friendship.create(:user_id => #user1.id, :friend_id => #user2.id) # 1
#user1.friends?(#user2).should be false
#user2.friends?(#user1).should be false
end
it "should be true for accepted friendship" do
Friendship.for_users(#user1, #user2).update_attribute(:approved, true) # 2
#user1.friends?(#user2).should be true
#user2.friends?(#user1).should be true
end
end
end
end
I am creating a friendship at the line marked # 1, and expect it to be present at line # 2, but I am guessing the database gets flushed in between the two.
Is this the wrong way of testing such a flow of events? What should I be doing? Any pointers would be much appreciated.
Yes, database gets flushed between the flow of events.
The best approach to testing is to have each test set up what it needs, and throw it away when it's done. That way each test is completely self contained, so you don't have to depend on previous tests for your later ones.
You may consider just building the friendship in factory girl and then testing that the friendship exists, and then make people friends through the UI in an integration test and check the same thing
I guess you are looking for context. Something like this:
context "when friends" do
before { Friendship.create ... }
it "should be false for requested friendship"
it "should be true for accepted friendship"
end
Sorry to say that but you should probably take a look on rspec docs: basic structure (describe/it)