Rspec 2 'should_receive' not working with Mongoid::Document class - ruby-on-rails

I'm facing some problems when trying to assert that a method in a Mongoid::Document class is invoked by my controller code:
require 'spec_helper'
describe AController do
describe 'GET index' do
it 'returns the full list' do
get :index
Model.should_receive(:find).with(:all)
response.code.should eq ("200")
end
end
end
Looking at test.log i can see the the query being executed against the database. BUT, the test fails with rspec complaining that Model.find(:all) was expected once, but received 0 times. Anyone got an idea of what is happening here? It seems to me that Rspec is not being able to stub classes that include Mongoid::Document.
Thanks!

Sorry, i screwed up, the expectation was supposed to be set before the get
Correct way:
Model.should_receive(:find).with(:all)
get :index
response.code.should eq ("200")

Related

Rspec expect passing when it should not

When I run the following test
RSpec.describe LessonsController, type: :controller do
describe 'GET / index' do
let(:lesson1) {FactoryGirl.create(:lesson)}
let(:lesson2) {FactoryGirl.create(:lesson)}
it 'returns an http success' do
get :index
expect(response).to be_success
end
it 'returns all the lessons' do
get :index
expect(assigns[:lessons]).to eq([])
expect(assigns[:lessons]).to eq([lesson1, lesson2])
end
end
end
The second expect, expect(assigns[:lessons]).to eq([lesson1, lesson2]), fails with expected: [#<Lesson id:...>, #<Lesson id:...>] got: #<ActiveRecord::Relation []>.
But then, when I run the following test and it all passes
RSpec.describe LessonsController, type: :controller do
describe 'GET / index' do
let(:lesson1) {FactoryGirl.create(:lesson)}
let(:lesson2) {FactoryGirl.create(:lesson)}
it 'returns an http success' do
get :index
expect(response).to be_success
end
it 'returns all the lessons' do
get :index
expect(assigns[:lessons]).to eq([lesson1, lesson2])
end
end
end
I am wondering why is it that the second test does not fail? I was expecting the second spec to also fail with the same reason as the first one.
I believe it might be due to the let statement.
With that said, I am running rspec-rails, factory_girl_rails and Rails 4. I don't believe it is due to contamination because this effect still occurs even when I run the test in isolation (focus).
First, I'm guessing your controller has some code like this:
#lessons = Lesson.all
Remember, that returns an ActiveRecord::Relation which may not actually hit the database until the last moment it needs to. Also, once an ActiveRecord::Relation fetches its results, it will not re-fetch them unless you call .reload.
Secondly, remember how let works. Code for a let isn't evaluated until you try to access that a variable. So, you get a situation like this:
describe "Something" do
let(:lesson) { Lesson.create! }
it "makes a lesson" do
# right now there are 0 lessons
lesson
# calling `lesson` caused the lesson to be created,
# now there is 1 lesson
end
end
Third, when you turn an ActiveRecord::Relation into an Array, it executes the real database query (in this case, select * from lessons).
With those things in mind, we can contrast the two test cases.
In the first case, lessons are fetched from the database before the lessons are actually created:
it 'returns all the lessons' do
get :index
# No lessons have been created yet
# `select * from lessons` returns no results
expect(assigns[:lessons]).to eq([])
# `lessons` is cached. It won't query the database again
# calling `lesson1` and `lesson2` creates two lessons, but it's too late
# the result has already been cached as []
expect(assigns[:lessons]).to eq([lesson1, lesson2])
end
In the second case, the lessons are created first, then the database query is executed:
get :index
# calling `lesson1` and `lesson2` creates two lessons
# then the AR::Relation runs a query and finds the two lessons
expect(assigns[:lessons]).to eq([lesson1, lesson2])
To demonstrate this, here is an example that should pass:
get :index
expect(assigns[:lessons]).to eq([])
# this causes the lessons to be created
lessons = [lesson1, lesson2]
# use `.reload` to force a new query:
expect(assigns[:lessons].reload).to eq(lessons)
Also, you could use RSpec's let! to create the lessons before running the example.

'No route matches' error while using Factory Girl on Rails

I've been trying to use FactoryGirl for tests on my Rails application, but I'm running into difficulty with it.
I feel as if there must be something fairly obvious I'm doing wrong, but after much searching I haven't been able to figure out the cause.
I'm trying to run a test to confirm the 'show' action is successful on one of my controllers.
Here's the error message I'm getting:
Failure/Error: get 'show'
ActionController::UrlGenerationError:
No route matches {:action=>"show", :controller=>"simple_requests"}
Below are the relevant code snippets leading to this outcome.
/spec/controllers/simple_requests_controller_spec.rb
require 'spec_helper'
describe SimpleRequestsController do
describe "GET 'show'" do
before do
#simple_request = build(:simple_request)
end
it "should be successful" do
get 'show'
expect(response).to be_success
end
end
end
/factories/simple_requests_controller_spec.rb
FactoryGirl.define do
factory :simple_request do
id 123
full_name "Testie McTesterson"
company "Test Company"
role "Analyst"
email "foobar#foobs.com"
phone "000888"
message "Test question?"
end
end
/controllers/simple_requests_controller.rb
def show
authorize SimpleRequest #For pundit
#simple_request = SimpleRequest.find(params[:id])
end
I have two hypotheses as to why this may be happening:
1) Rspec is looking for an id for the 'show' action, but somehow can't find it. (Although there is one in the Factory, and I've yet to figure out how it wouldn't be flowing through.)
2) Pundit is causing issues, since the show action may require authorization (although commenting out the 'authorize' line makes no difference at present)
Any and all thoughts welcome :)
EDIT
Pasting below the output of rake routes | grep simple_requests
simple_requests GET /simple_requests(.:format) simple_requests#index
POST /simple_requests(.:format) simple_requests#create
new_simple_request GET /simple_requests/new(.:format) simple_requests#new
edit_simple_request GET /simple_requests/:id/edit(.:format) simple_requests#edit
simple_request GET /simple_requests/:id(.:format) simple_requests#show
PATCH /simple_requests/:id(.:format) simple_requests#update
PUT /simple_requests/:id(.:format) simple_requests#update
DELETE /simple_requests/:id(.:format) simple_requests#destroy
Edit 2 - Adding ID parameter
I have now also attempted to add an id as follows:
it "should be successful" do
get 'show', id: #simple_request.id
expect(response).to be_success
end
This time I received the following error message
ActiveRecord::RecordNotFound: Couldn't find SimpleRequest with 'id'=123
'123' is the ID in my /factories - I think I must be missing something to get this working, but can't figure out what yet.
Your SimpleRequest does not have an Id. You need to use create instead of build
before do
#simple_request = create(:simple_request)
end
it "should be successful" do
get 'show', id: #simple_request.id
expect(response).to be_success
end
Try this:
before do
#simple_request = create :simple_request
end
it "should be successful" do
get 'show', id: #simple_request.id
expect(response).to be_success
end
It's a show view, so you'll have to supply your request with an ID and you'll have to actually create a record with create instead of build in your before block.

Rspec matching ActiveRecord result sets

UPDATED: I realise now that I've been misreading the diff, and I have a string or symbol on one side of the comparison. Still unsure how I should be putting the expectation in this test however..
I'm new to Rspec and TDD in general, and I've run into this problem. I have a controller that does this:
def index
#users = User.page(params[:page])
end
(I'm using Kaminara to paginate)
And a spec:
describe "when the user DOES have admin status" do
login_admin_user
it "should allow the user access to the complete user list page" do
get :index
response.response_code.should == 200
end
describe "and views the /users page" do
before(:each) do
User.stub(:page) {[ mock_model(User), mock_model(User), mock_model(User) ]}
end
it "should show all users" do
get :index
assigns (:users).should =~ User.page
end
end
end
The spec fails with the following:
Failure/Error: assigns (:users).should =~ User.page
expected: [#<User:0x5da86a8 #name="User_1004">, #<User:0x5d9c90c #name="User_1005">, #<User:0x5d93ef6 #name="User_1006">]
got: :users (using =~)
Diff:
## -1,4 +1,2 ##
-[#<User:0x5da86a8 #name="User_1004">,
- #<User:0x5d9c90c #name="User_1005">,
- #<User:0x5d93ef6 #name="User_1006">]
+:users
Those result sets look identical. Why does this spec fail? Thanks in advance!
I think the problem is the space after assigns. It's comparing the symbol :users to your list. Change it to:
assigns(:users).should =~ User.page
And just a note on how to read Rspec failures. The part after expected, is what you gave to should, whereas the part after got is the value your code actually produced. So it's clear from the report that the result sets were not identical.

shoulda, rspec, guard, spork issues with http responses

Hello and thanks for you patience!
My rails app uses a combination of rspec and shoulda to run tests. The tests are automated over guard and spork. One of my controllers tests looks like
it {should respond_with(:success)}
When running tests i get
Expected response to be a 200, but was 301
manually testing by browsing & wget things go right, the page is responding correctly with 200 status code. As I am quite new to rails testing, proberly I am not understanding how the tests are currently ran. How are they implemented? What was the purpose of the environment 'test'? Is there some kind of webserver running in backgroud to run the tests? Obviously there is some kind of non-wanted redirecting.
Thanks in advance!
Edit: More sources
controller:
class PlansController < ApplicationController
def index
#plans=Plan.all
end
... more methods ...
end
test:
describe PlansController do
before :each do
#plan=FactoryGirl.create(:plan)
end
context " get :index" do
before do
get :index
end
it {should respond_with(:success)}
end
... more tests..
end
You are missing an :each in the before block of the context for get :index so you are never calling the index action.
Update as follows:
context " get :index" do
before(:each) do
get :index
end
it { should respond_with(:success) }
end

RSpec Rails Login Filter

I recently switched started using rspec-rails(2.6.1) with my Rails(3.0.8) app. I'm used to Test::Unit, and I can't seem to get a filter working for my test methods. I like to keep things as DRY as possible, so I'd like to set up a filter that I can call on any test method that will login as an Authlogic user before the test method is called. I tried accomplishing this by using an RSpec filter in spec_helper.rb:
config.before(:each, :login_as_admin => true) do
post "/user_sessions/create", :user_session => {:username => "admin", :password => "admin"}
end
Then I use it in the corresponding test method(in this case spec/controllers/admin_controller_spec.rb):
require 'spec_helper'
describe AdminController do
describe "GET index" do
it("gives a 200 response when visited as an admin", :login_as_admin => true) do
get :index
response.code.should eq("200")
end
end
end
However, I get this error when I run rspec spec:
Failures:
1) AdminController GET index gives a 200 response when visited as an admin
Failure/Error: Unable to find matching line from backtrace
RuntimeError:
#routes is nil: make sure you set it in your test's setup method.
Blech. Can I only send one HTTP request per test? I also tried stubbing out my authenticate_admin method(inside the config.before block), without any luck.
Unfortunately, there is no way at the moment to do what you're trying to do in a globally defined before hook. The reason is that before hooks are executed in the order in which they get registered, and those declared in RSpec.configure are registered before the one that rspec-rails registers internally to set up the controller, request, response, etc.
Also, this has been reported to https://github.com/rspec/rspec-rails/issues/391.
You should use shulda's macrons. To use shoulda modify your spec_helper.rb
RSpec.configure do |config|
config.include Clearance::Shoulda::Helpers
end
And then can setup filter in controller spec like
require 'spec_helper'
describe AdminController do
fixture :users
before(:each) do
sign_in_as users(:your_user)
end
describe "GET index" do
it("gives a 200 response when visited as an admin", :login_as_admin => true) do
get :index
response.code.should eq("200")
end
end
end

Resources