How to move from requests to features in RSpec - ruby-on-rails

I have installed a rspec-rails 3.0.0.beta1 (on ruby2 + rails4) and had some troubles using devise helper in my request specs. After some googling, I've found that I need to move all my specs from spec/requests to spec/features (the requests dir was created by rspec installator or scaffold generator [not sure right now], so I'm a bit confused). That made my devise helper working but there are more issues instead.
Here are three scenarios:
Spec file is spec/requests/events_spec.rb and dont have any type set
undefined method 'visit' for #<RSpec::ExampleGroups::Events::GETEvents:0x007ff2464d9848>
Spec file is spec/requests/events_spec.rb and has a type: :controller
it throws an error undefined method 'events_path' for nil:NilClass when i'm trying to use a get events_path method(s)
Spec file is spec/features/events_spec.rb and dont have any type set
undefined method `get' for #<RSpec::ExampleGroups::Events::GETEvents:0x007ffb2714a968>
Spec file is spec/features/events_spec.rb and have a type: :controller
undefined method `events_path' for nil:NilClass
I think I can find some tweaks on the internet but I'm a fresh rspec user and I feel like I'm doing something extremely wrong. And all the examples online are not related to my problem.
The code is here: https://gist.github.com/mbajur/8002303

As of Capybara 2.0, the Capybara methods (e.g. visit) are only available by default for feature specs, not request specs or controller specs.
Similarly, the get method is not available for feature specs, only controller and request specs.
Finally, the path helper methods are only available in request or feature specs.
Given that, your failures can be explained as follows:
No Capybara, because it's not a feature spec.
No path helper methods because it's a controller spec.
No get method because it's a feature spec.
No path helper method because it's a controller spec.
You need to either:
Make it a request spec and configure RSpec to include Capybara::DSL for request specs
Leave it a feature spec and stop using methods like get which aren't intended for use with feature specs
Here's some interesting background on introducing feature specs as distinct from request specs

Related

'assigns' method not found in rspec capybara

I have the following code in my controller:
private
def remaining_words
#remaining_words = Vocab.all.where.not(id: session[:vocab_already_asked])
#questions_remaining = #remaining_words.length - 4
#quiz_words = #remaining_words.shuffle.take(4)
And here is my test:
feature 'Quiz functionality' do
scenario "gets 100% questions right in quiz" do
visit(root_path)
visit(start_quiz_path)
assigns(:questions_remaining).length.to_i.times do
orig_value = find('#orig', visible: false).value
choose(option: orig_value)
click_on('Submit')
expect(page).to have_content('You got it right!')
expect(page).not_to have_content('Sorry, wrong answer!')
end
expect(page).to have_content("Your score is 27/27")
save_and_open_page
end
end
I get the error message when I run the test:
NoMethodError: undefined method `assigns' for #<RSpec::ExampleGroups::QuizFunctionality:0x007f8f2de3f2b0>
# ./spec/features/quizzes_spec.rb:9:in `block (2 levels) in <top (required)>'
I've also tried using controller.instance_variable_get(:remaining_words) and get this error message
NameError:
undefined local variable or method `controller' for #<RSpec::ExampleGroups::QuizFunctionality:0x007fc4b99251a0>
Am I missing something in setting up the test? Should I be using describe instead of feature to enable the assign method?
assigns was solely available in controller tests - it was depreciated in Rails 5.
Testing what instance variables are set by your controller is a bad
idea. That's grossly overstepping the boundaries of what the test
should know about. You can test what cookies are set, what HTTP code
is returned, how the view looks, or what mutations happened to the DB,
but testing the innards of the controller is just not a good idea.
- David Heinemeier Hansson
In RSpec controller specs wrap the deprecated ActionController::TestCase.
A controller spec is identified by having the type: :controller metadata.
RSpec.describe ThingsController, type: :controller do
# ...
describe "GET #index" do
end
end
If you have set config.infer_spec_type_from_file_location! in config.infer_spec_type_from_file_location! RSpec will infer that any spec in spec/controllers has type: :controller.
You should avoid controller specs for new applications in favor of request and feature specs. One of the main problems with controller specs besides the violation of encapsulation is that the entire request phase is stubbed, the request does not actually go through rack or the routes which can mask routing errors and means that Rack middleware like Warden (used by Devise) or sessions must be stubbed.
If you have a legacy application you can reintroduce assigns with a gem. If you are just learning RSpec you should select more up to date tutorials.
Feature specs are high-level tests meant to exercise slices of
functionality through an application. They should drive the
application only via its external interface, usually web pages.
https://relishapp.com/rspec/rspec-rails/v/3-7/docs/feature-specs
Use feature specs for high level tests centered on the user story. Use RSpec.feature "New Cool Feature" to write a feature spec.
Request specs provide a thin wrapper around Rails' integration tests,
and aredesigned to drive behavior through the full stack, including
routing (provided by Rails) and without stubbing (that's up to you).
https://relishapp.com/rspec/rspec-rails/v/3-7/docs/request-specs/request-spec
Use RSpec.describe "Some resource", type: :request to write a feature spec.
Request specs are invaluable for testing API' or when you just need fast tests that ensure that the correct mutations happened to the DB or that the correct http responses are sent.
See:
https://blog.bigbinary.com/2016/04/19/changes-to-test-controllers-in-rails-5.html
https://github.com/rails/rails/issues/18950
You're writing feature specs/integration tests which don't have access to the controller/controller instance variables. They are meant to be more of a black box test executed from the users perspective. When setting up the data for the test you should know how many questions need to be asked and then either hardcode that in your test, or, better yet, detect based on the page contents whether there are more questions to answer (just like a user would have to).

Why would Guard-launched and manually-launched specs behave differently?

When I run view specs manually (zeus rspec or rake spec) I am getting errors raised if Devise or CanCanCan helpers are present in the view
Failure/Error: <% if user_signed_in? %>
ActionView::Template::Error: undefined method `authenticate' for
nil:NilClass
Failure/Error: <% if can? :update, #object %>
ActionView::Template::Error: undefined method `authenticate' for
nil:NilClass
When the same specs are run via Guard, no errors are raised and the specs pass.
Adding the following to the view spec causes both Guard and manually launched specs to pass.
...
#ability = Object.new
#ability.extend(CanCan::Ability)
controller.stub(:current_ability) { #ability }
controller.stub(:user_signed_in?) {false}
...
(I also tried including config.include Devise::TestHelpers, type: :view in a support file but this did not appear to do anything)
Why would Guard-launched and manually-launched specs behave
differently?
Are these controller stubs the 'correct' way to deal
with this issue?
Why would Guard-launched and manually-launched specs behave differently?
The best way is to run Guard in debug mode: bundle exec guard -d. It will show you the actual rspec command being run. Then, you can compare that to how you're invoking rspec.
The second thing could be: you may be running different versions of gems. Try bundle exec rspec vs bundle exec guard. There shouldn't be differences. You can then experiment with zeus, etc.
Are these controller stubs the 'correct' way to deal with this issue?
I'd say it's best to use Cucumber/Capybara for testing views. It isn't as fast and the effort to mock the views is probably just not worth the time.
But otherwise, it doesn't matter how you stub/mock as long as you're testing what matters. For views, I think that means testing to make sure the right instance variables are used to generate the views. Testing the actually html generated probably isn't too convenient.
Views change often, too - so too much testing there can be a waste of time with little gain. Even if you make errors in views, they're usually easy to spot, quick to fix and there aren't too many edge cases. (Those are usually handled by controllers). So again, using Cucumber+Capybara gives you more tools, more flexibility and enough coverage without sacrificing your freedom to change things without being forced to update tests as well.
The reason for devise helpers is to get sign_in and sign_out working. That's pretty much it. So if those don't work, you likely don't have the helper included.
I also tried including config.include Devise::TestHelpers, type: :view in a support file but this did not appear to do anything
Are you sure that support file was actually included? Usually nowadays you have to do that explicitly in the spec_helper.rb file.

Rspec 3.X: native built in html matchers? Or do we have to use Capybara?

I've been reading a ton of docs and SO questions/ answers on all the changes as Rspec has evolved, want to be sure of the answer...
My goal is to use native Rspec-rails (I have 3.2.2) to do integrated controller/view tests that look for 1) CSS classes and 2) ID selectors. In other words given this view snippet:
<!-- staticpages/dashboard -->
<div class="hidden">Something</div>
<div id="creation">This</div>
This should pass (however it should be semantically written):
describe StaticpagesController do
render_views
it "should find everything" do
get :dashboard
expect(response.body).to have_selector("div#creation")
expect(response.body).to have_css("hidden")
expect(response.body).to_not have_selector("div#nothinghere")
end
end
I would like to do this without additional gems like Capybara; is that possible?
Here's a high level of what I've learned so far:
in Rspec 1, the have_tag feature allowed you to do this (http://glenngillen.com/thoughts/using-rspec-have-tag)
in Rspec 2, the have_tag was replaced with webrat's have_selector (have_tag vs. have_selector)
in Rspec 3, webrat support has been removed (http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/)
In my own experimentation, the code above generated:
Expect<long response.body here>.to respond to `has_selector?`
So that has indeed been deprecated. Still, I'd love to know if there's some other way to do this that I don't know about.
IF it turns out I need Capybara to do these fancy matchers, is there a way to do this in my integrated controller/view specs? My understanding is that I have to add type: :feature to the describe StaticpagesController line to use Capybara's matchers. However, the minute I do that, render_views is no longer available (since it's limited to type: :controller). Note, render_views also dies if, per this post (https://www.relishapp.com/rspec/rspec-rails/v/2-99/docs/controller-specs/use-of-capybara-in-controller-specs), I manually include Capybara::DSL into my controller spec. Anyway, I would really like to not have to rewrite my current controller specs into a bunch of feature specs...
It would seem that you want feature specs (with Capybara) more than controller specs as you're not testing any of the things controller specs are typically used to test such as:
whether a template is rendered
whether a redirect occurs
what instance variables are assigned in the controller to be shared with the view
the cookies sent back with the response
Also, you probably want to consider writing feature specs for new apps over controller specs since controller tests will probably be dropped in Rails 5 in favor of the writing of integration/feature tests.
For a description of the different kinds of specs that you could write, and what they're typically used for,
see this SO answer.

undefined method `rspec_reset' with rspec version 2.14

I am trying execute test cases with rspec version 2.14 for which I am getting following error
undefined method `rspec_reset' for
I am trying to use rspec_reset on the class. The same test cases are working with rspec 2.13.1. So is it possible that rspec_reset method is not available after 2.13?
The reset method does not exist in RSpec 2.14.x. Instead, it is a helper method defined inside the spec_helper.rb file for the rspec-mocks project.
module VerifyAndResetHelpers
def verify(object)
RSpec::Mocks.proxy_for(object).verify
end
def reset(object)
RSpec::Mocks.proxy_for(object).reset
end
end
You can see that this method delegates the reset action to the underlying proxy instead of tacking it on to the class definition of the object in question.
Yes, in 2.14, rspec_reset is no longer available on all objects as it was previously, as discussed in https://github.com/rspec/rspec-mocks/pull/250.
Although I can't find any documentation on it, there now appears to be an RSpec class method reset which takes an object as an argument and will effectively "undo" any RSpec operations that have been done to that object.
There's an RSpec "example" at https://github.com/rspec/rspec-mocks/blob/cee433c89125a3984df33c87eb61985613adce9b/spec/rspec/mocks/mock_spec.rb which still uses the rspec_reset in the description of the example, but which now uses the aforementioned reset method to do the reset. In earlier versions of the example, the reset was done with rspec_reset.

main_app Namespace unknown in Rspec, only in unit suite tests (batch)

I have a helper class, ApplicationHelper, that has a method, build_links(). I have another class, AppleClass, that refers to that method.
AppleClass
def foo
....
build_links
end
end
ApplicationHelperClass
def build_links
main_app.blah_path(1)
end
end
The complication here is that there's an Engine, so I usually explicitly reference "main_app.blah_path" not just "blah_path".
The test against foo passes by itself, in its file, and when I run all helpers. It fails, though, when I include it in all the unit tests - "rake spec:suite:unit", and with our entire suite. All Apple tests pass, all ApplicationHelper tests pass. The only failing ones are when one method is referring to the other method, in routes, outside of the engine, in the full suite.
`undefined local variable or method `main_app' for #
<RSpec::Core::ExampleGroup::Nested_45::Nested_1:0x007fc134b30130>`
My suspicion is that the test helper, or some config, is not loading the engine's routes early enough, and thus links to "main_app" don't make sense. If I remove main_app, the test fails until it's run in the main suite.
Does anyone have tips on troubleshooting what's really going on? Also, could I kickstart the routing somehow in test_helper?
ruby-1.9.3-p385, rails 3.2.13, rspec 2.13.0
I had the same issue and found that if I added this method to the top of my Controller RSpec test case, then it resolved the issue entirely.
def main_app
Rails.application.class.routes.url_helpers
end
I think this issue is related to this question

Resources