For almost all my specs, when rspec reports an error, it informs me of the line number at the end of the path e.g.
rspec ./spec/controllers/eclubs_controller_spec.rb:21
However in one of my specs, it reports the error location like this
rspec ./spec/controllers/eclubs/members_controller_spec.rb[1:1:2:3:1]
which may make sense in terms of the nesting of blocks but frankly is rather cryptic.
The top part of the spec that works looks like this
require 'rails_helper'
describe EclubsController do
and the one that does not work looks like this
require 'rails_helper'
describe Eclubs::MembersController do
The only difference I can see in the two files is that one controller is namespaced, but I have other namespaced controllers that report the error line correctly.
What is causing this?
RSpec uses the example id when the line number is not sufficiently unique to identify the example in question.
This can happen when examples are dynamically defined, for example in a loop:
(0..10).each do |i|
it do
expect(i).to_not eq(5)
end
end
# rspec './my_spec.rb[1:6]'
Or when using a shared example group:
RSpec.shared_examples_for "not equal 5" do |i|
it do
expect(i).to_not eq(5)
end
end
RSpec.describe do
it_behaves_like "not equal 5", 5
it_behaves_like "not equal 5", 4
end
# rspec './my_spec.rb[2:1:1]'
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 am trying to learn how to use Rspec's shared examples feature and am getting a warning when I run my tests:
WARNING: Shared example group 'required attributes' has been previously defined at:
/Users/me/app/spec/support/shared_examples/required_attributes_spec.rb:1
...and you are now defining it at:
/Users/me/app/spec/support/shared_examples/required_attributes_spec.rb:1
The new definition will overwrite the original one.
....
I have read what I think is the documentation on this problem here but I'm having trouble understanding it/seeing the takeaways for my case.
Here is my shared example:
# spec/support/shared_examples/required_attributes_spec.rb
shared_examples_for 'required attributes' do |arr|
arr.each do |meth|
it "is invalid without #{meth}" do
subject.send("#{meth}=", nil)
subject.valid?
expect(subject.errors[meth]).to eq(["can't be blank"])
end
end
end
I am trying to use this in a User model and a Company model. Here is what it looks like:
# spec/models/user_spec.rb
require 'rails_helper'
describe User do
subject { build(:user) }
include_examples 'required attributes', [:name]
end
# spec/models/company_spec.rb
require 'rails_helper'
describe Company do
subject { build(:company) }
include_examples 'required attributes', [:logo]
end
Per the recommendations in the Rspec docs I linked to above, I have tried changing include_examples to it_behaves_like, but that didn't help. I also commented out company_spec.rb entirely so there was just one spec using the shared example, and I am still getting the warning.
Can anyone help me see what's really going on here and what I should do in this case to avoid the warning?
I found the answer in this issue at the Rspec Github:
Just in case someone googles and lands here. If putting your file with
shared examples into support folder has not fixed the following
error...Make sure your filename does not end with _spec.rb.
As a followup to this, I had the issue in an included shared context with a filename that did not end in _spec.rb and was manually loaded via require_relative, not autoloaded. In my case, the issue was a copy-paste problem. The test looked like this:
RSpec.shared_examples 'foo' do
RSpec.shared_examples 'bar' do
it ... do... end
it ... do... end
# etc.
end
context 'first "foo" scenario' do
let ...
include_examples 'bar'
end
context 'second "foo" scenario' do
let ...
include_examples 'bar'
end
end
The intent was to provide a single shared set of examples that exercised multiple contexts of operation for good coverage, all running through that internal shared examples list.
The bug was simple but subtle. Since I have RSpec monkey patching turned off (disable_monkey_patching!), I have to use RSpec.<foo> at the top level. But inside any other RSpec blocks, using RSpec.<foo> defines the entity inside RSpec's top-level :main context. So, that second set of shared, "internal" examples weren't being defined inside 'foo', they were being defined at the top level. This confused things enough to trigger the RSpec warning as soon more than one other file require_relative'd the above code.
The fix was to just do shared_examples 'bar' in the nested set, not RSpec.shared_examples 'bar', so that the inner examples were correctly described inside the inner context.
(Aside: This is an interesting example of how having monkey patching turned off is more important than might at first glance appear to be the case - it's not just for namespace purity. It allows for a much cleaner distinction in declarations between "this is top level" and "this is nested".)
Often when feature testing in RSpec/Cucumber I want to emulate a sequence of related procedural user actions. For instance, a user might sign in, then update profile, then take some other action on the site, then sign out, and so on...
In RSpec, an analogous feature test might look something like this:
describe "Step 1: Sign in" do
before do
# Capybara logic
end
describe "Step 2: Visit settings page" do
before do
end
describe "Step 3: Update profile" do
before do
# More Capybara/etc...
end
describe "Step 4: Making a new post" do
before do
# ......
end
describe "Step 5: Viewing inbox" do
before do
# ...
# More nested code etc, etc, etc...
end
end
end
end
end
end
However, clearly this nesting very quickly gets out of control, especially when testing complex behavior on a site. Furthermore, the "before" blocks in the outermost nesting get run more times than then inner before blocks, unnecessarily slowing down the test suite.
So, is there perhaps a more elegant way to handle this? I'm able to clean things up slightly by using shared_examples_for, it_behaves_like, shared_context, before(:all), helper methods, etc. But it seems rather awkward when all I want to do is run the tests procedurally without each test needing to run in isolation. In other words, I'm looking for a test DSL like this:
# Step 1:
test "Sign in" do
visit "/sign_in"
fill_in "whatever"
# ...
end
# Step 2:
test "Update profile", after: "Sign in" do
# Runs after "Sign in" sharing same context so variables and object states aren't reset
end
And yes I know in principle tests run in isolation, but let's be fair, doing so isn't always practical when each step in the test may rely on the outcome of a prior test.
Specs work great when grouped by requirements. It DRY's up the setup/teardown for each condition and makes them easy to scan when you know what the case is but don't know what the functionality would be.
For example:
describe "a logged in user" do
before do
# log in
end
describe "with 3 items" do
before do
# add 3 items
end
it "displays 3 items in the cart" do
end
end
describe "with 0 items" do
it "displays an empty cart" do
end
end
end
I don't think nesting specs works well for describing implementation (e.g. first do this, then do that). That seems to explode out really quickly
I have a test that looks like this:
class PageTest < ActiveSupport::TestCase
describe "test" do
test "should not save without attributes" do
page = Page.new
assert !page.save
end
end
end
When running the tests, I get 0 tests, 0 assertions. If I remove the describe "test" do, I get the 1 test, 1 assertions. So I have the feeling that the describe "..." do is actually making the test disappear.
What is going on here? What am I missing?
Looks like you're mixing up minitest specs and ActiveSupport::TestCase. If you check the rails guides on testing the test method is explained but it's not used with describe.
Rails adds a test method that takes a test name and a block. It
generates a normal MiniTest::Unit test with method names prefixed with
test_. So,
test "the truth" do
assert true
end
acts as if you had written
def test_the_truth
assert true
end
The describe syntax is explained in the minitest docs under the spec section and is used with it (and not test). Like so:
describe "when asked about cheeseburgers" do
it "must respond positively" do
#meme.i_can_has_cheezburger?.must_equal "OHAI!"
end
end
I have a still pretty simple Rails application that I want to develop using BDD with Cucumber and TDD with RSpec. Currently, I am hanging at a test where I want to check that if a new instance of an Organizer (that's the model I have) cannot be created due to a validation error. I would like to check that the errors Array of the object to be created is not empty so that I can be sure that error messages are available for showing them in the view.
require 'spec_helper'
describe OrganizersController do
render_views
describe "POST 'create'" do
describe "with invalid arguments" do
before(:each) do
request.env["HTTP_REFERER"] = organizers_new_path
#organizer_args = { :name => "" }
end
it "should return a non-empty list of errors" do
post 'create', :organizer => #organizer_args
#organizer.errors.empty?.should_not be_true
end
end
end
end
I am developing based on Rails 3.2.9 with RSpec 2 and cucumber-rails.
Any suggestions are appreciated. Thanks!
You should use assigns method to get instance variable from controller action:
assigns(:organizer).errors.empty?.should_not be_true
The latest preferred syntax is:
expect(assigns(:organizer).errors.empty?).to_not be_true
thanks for the answer guys but I'd like to suggest a slightly nicer syntax:
expect(assigns(:organizer).errors).to_not be_empty
(unrelated to the question 👇)
Basically whenever you have a method that ends with ? you'll have the corresponding rspec matcher that starts with be_ e.g.
1.odd? #=> true
expect(1).to be_odd