Rails Engine - RSpec tests - ruby-on-rails

I my main rails project I have mounted a engine of common code which does common tasks like currency conversion.
I can reference the methods in this common engine like this:
MyCommon::CurrencyHelper.convert_to_currency
This works fine in the project code, but does not seem to be working when I change the RSpec tests to point to the common methods.
The tests fail as it cannot find the common methods:
NoMethodError: undefined method `convert_to_currency' for MyCommon::CurrencyHelper:Module
./spec/helpers/application_helper_spec.rb:211:in `block (3 levels) in <top (required)>'
Do I need to do something else?

If by "I change the RSpec tests to point to the common methods" you mean that you are running the RSpec tests from engines/my_common directory (the engine's tests found in engines/my_common/spec) then you can find/reference your convert_to_currency method like this CurrencyHelper.convert_to_currency in your tests.
So for example, Stuff::BookingSlot.book_a_slot! (where Stuff is the engine) would be tested like this:
it 'book_a_slot! marks a slot as unavailable when a host is available' do
ai = FactoryGirl.create(:booking_slot, available: false, host_id: 1)
ai2 = ai.clone
ai2.update_attributes(available: true, host_id: 2)
BookingSlot.book_a_slot!
expect(ai2.reload.available).to eq false
end

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 views fail with Rails 4 mountable engine

I've created a Rails 4 app with a mountable Rails 4 engine called Foobar. That engine contains a scaffold for Items.
After setting up rspec-rails in the engine, I am unable to get the view specs to pass.
I get the following error:
2) foobar/items/index renders a list of items
Failure/Error: render
ActionView::Template::Error:
undefined method `item_path' for #<#<Class:0x007fe6dae7b3c8>:0x007fe6da9a2ec8>
# ./app/views/foobar/items/index.html.erb:21:in `block in ___sers_ianneub__ocuments__ode_rails_engine_spec_test_engines_foobar_app_views_foobar_items_index_html_erb__1719273292435665376_70314743713380'
# ./app/views/foobar/items/index.html.erb:16:in `each'
# ./app/views/foobar/items/index.html.erb:16:in `___sers_ianneub__ocuments__ode_rails_engine_spec_test_engines_foobar_app_views_foobar_items_index_html_erb__1719273292435665376_70314743713380'
# ./spec/views/foobar/items/index.html.erb_spec.rb:20:in `block (2 levels) in <top (required)>'
When starting up the rails server in either the outer application or the engine's spec/dummy app the views all seem to work just fine.
So my question is:
Why do the rspec tests fail, even though the engine views work fine from the rails server?
Bonus question:
What can I change in the index.html.erb to make the test pass?
I'm really trying to understand what is at play here in the rspec tests. IMHO it seems like there is an issue with rspec (and/or rspec-rails) that prevents this from working. Like something isn't being loaded in the tests that would normally make this work inside the rails server. In other words: if it works in the rails server, then it should work in rspec the same way. Would that be correct? What am I missing?
The app is available here:
https://github.com/ianneub/rails-engine-spec-test
You should be able to clone the repo and run these commands to duplicate the error:
cd engines/foobar
bundle install
rspec
Thank you in advance for any help and guidance that you share with me (and the world).
Line 21 in the 'index.html.erb' view needs scoping to the foobar gem.
<td><%= link_to 'Show', foobar.item_path(item) %></td>
That's because Rspec renderer do not include url_helpers methods, but rails renderer somehow included these methods. Could be fixed by
a before hook:
RSpec.configure do |config|
config.before(:example, type: :view) do
view.class_eval do
include <Your Engine Namespace>::Engine.routes.url_helpers
end
end
end

Rspec stubbing results in Mocha errors

Not quite sure what's going on here. I'm moving over some code from another project of mine and suddenly the same specs from before are generating errors in the new project. All the errors appear to revolve around calling the stub method. Here's an example test:
it "retrieves active workers from Redis" do
#monitor.should_receive(:monitor_running?).and_return(false)
REDIS.should_receive( :smembers ).with( 'leaderboard-workers' ).and_return( [] )
#monitor.perform
end
This works. However if I switch the first test line to this:
#monitor.stub(:monitor_running?).and_return(false)
I end up with the following error:
1) LeaderboardMonitor#perform retrieves active workers from Redis
Failure/Error: #monitor.stub(:monitor_running?).and_return(false)
Mocha::ExpectationError:
unexpected invocation: #<Mock:0x7fcc18c8bab8>.and_return(false)
satisfied expectations:
- allowed any number of times, not yet invoked: #<Mock:0x7fcc18c8bab8>.monitor_running?(any_parameters)
# ./spec/workers/leaderboards/leaderboard_monitor_spec.rb:58:in `block (3 levels) in <top (required)>'
I'm not quite sure what's going on here. Is this an issue with Mocha overriding the stub method? How do I work around this?!?!?
I don't know what version of mocha you are using. Have you tried something like
#monitor.expects(:monitor_running?).returns(false).at_least_once

Rspec method not found when using model subfolders

Hello i am receiving this error:
spec/models/stores/persistent_spec.rb:6:in block (2 levels) in <top
(required)>': undefined methodexpect' for #
(NoMethodError)
Here is my setup. i have a class called Store located inside app/models
i have a class called Stores::Temporary < Store placed inside app/models/stores
I am trying to write tests for Temporary and they are failing with the error above
i have created temporary_spec.rb inside spec/models/stores and the code for it is the following:
require "spec_helper"
describe Stores::Temporary do
end
i am trying to write an expect to raise_error statement.
I have also tried 5.should == 5 which results with no available test being detected inside this file. I am using guard to autorun the tests, so changes in the files are being detected (works correctly) and tests are rerun but appear as blank.
So after struggling with this for 2 hours i restarted the PC and it was working without changing anything... One of those stupid errors you just need to sorta sleep-over and retry.

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