Why would Guard-launched and manually-launched specs behave differently? - ruby-on-rails

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.

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).

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.

Faking instance variable in RSpec and Capybara feature spec

I'm trying to set up some feature specs before I get into refactoring some of my company's old code. It's kind of an unconventional setup, but I was able to figure out enough about test doubles to bypass the authentication enough to get started. One problem I'm still having is that some of the instance variables set in these methods I'm bypassing are expected by the view, so I get undefined method for nil:NilClass errors. I would like to get the specs running before I make any changes to the program code. In this case, I could easily just move the particular instance variable to another method. But I'm sure more situations like this will come up. Here's the example I'm currently working on:
def security_level
#right_now = Time.now
#
# other code that wont work without
# connecting to a remote authentication
# server
#
end
Then in my spec:
feature 'Navigation' do
before(:each) do
allow_any_instance_of(ApplicationController).to receive(:security_level).and_return(nil)
end
scenario 'is possible' do
visit root_path
expect(page.has_content?('Quick Stats'))
end
end
Here's the error, coming from #right_now.year in the view
Failure/Error: visit root_path
NoMethodError:
undefined method `year' for nil:NilClass
# ./common/views/layouts/bootstrap/layout.haml:63
EDIT: Is there a way to set instance variables on the controller from within a feature spec?
There's no easy way to accomplish what you want.
The feature spec is handled mostly by Capybara, not RSpec. Capybara runs the majority of the browser / rails server behavior in an external process. This make it inaccessible from RSpec's point-of-view. Thus you cannot use stubs / doubles in this manner.
Feature specs are largely meant to be end-to-end acceptance tests. The idea is to exercise your system as those who would use your system do. Generally, in these types of specs you perform various "workflows". This means, having the spec, log a user in, navigate to particular pages, filling forms, clicking buttons and links. You then generally make your expectations on what you see in the view.
This means your spec would look more like:
feature 'Navigation' do
let(:regular_user) { User.create!(name: 'A Regular User') }
def sign_in(a_user)
visit sign_in_url
# fill out form
click_button 'Sign In'
end
before(:each) do
sign_in(regular_user)
end
scenario 'is possible' do
visit root_path
expect(page.has_content?('Quick Stats'))
end
end
https://github.com/per-garden/fakeldap may provide enough ldap functionality for your feature tests.

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

Running rspec from rails application code

I've got a situation where I need to validate some regular expressions.
So, during my application run, I may want to test that a particular regex:
Contains no spaces
Contains only a certain number of capture groups
Does not use certain characters
Only contains a certain number of wildcards
rspec seems like the perfect tool for doing this. I realize that it's typically used to test application interfaces, assumptions and logic before an application is run, however. But, the natural syntax combined with the automatic reporting output would be nice to have.
Questions:
Is this an appropriate use of rspec?
How can one call a description from within a running application?
Or, should I abandon this approach and simply write methods within my class to perform the validations?
Using rspec in this way is highly discouraged and unusual. You should leave testing code in a :test group in your Gemfile and not reference it in your app.
Instead, use rails validations that your field matches a regex format, and then write tests in rspec to verify your validations.
This is definitely something new: using rspec inside rails for validation. But for specific problems one tends to propose to use a DSL, and as such rspec is a DSL which might just perfectly suited for your job.
So if that is the case: why not, yes, go ahead. Be creative and find new ways to use the tools you have.
Just a small warning: from the few points you marked, the complexity does not seem to be too big, so make sure you are not using a bazooka to kill a fly. Rspec is a big and very powerful tool, tying in rspec to run during the rails process might not be entirely straightforward.
If you want to generate a report, you could use the global after(:all) { puts "report goes here" } or after(:each). If you expect some of your data to blow up your tests, you can test for .should raise_exception. I imagine you'd be writing lots of exception handling to keep the expected failures out of the output. Logging the results to a database or a file might also be annoying. If you can, describe the test that you are doing on the data and then just parse the output of rspec at the end.
class Car
attr_accessor :doors
end
describe "Car" do
it "should have doors" do
Car.new.should respond_to(:doors)
fail("failing intentionally")
end
it "should pass this easily" do
Car.new should_not be nil
end
after(:all) { puts "report here" }
end
You can see below that I have a description of the test that failed.
$ rspec rspec_fail.rb
F.report here
Failures:
1) Car should have doors
Failure/Error: fail("failing intentionally")
RuntimeError:
failing intentionally
# ./rspec_fail.rb:9:in `block (2 levels) in <top (required)>'
Finished in 0.00052 seconds
2 examples, 1 failure
I would be easy enough to just make a report of the failures if this was testing text and regex's. Failure/Error: fail("Data has spaces") etc.

Resources