Mocha not mocking a class method in a functional test (Rails 3) - ruby-on-rails

In a rails 3 app, I'm using mocha to do some mocking in my functional tests. However it doesn't seem to mock a class method in the functional controller.
Controller code
class TagsController < ApplicationController
respond_to :json
def index
response = User.tags_starting_with(params[:query])
respond_with response
end
end
Functional test
class TagsControllerTest < ActionController::TestCase
context "index action with query" do
setup do
query = "A_QUERY"
get :index, :query => query, :format => "json"
#tags = ["these", "are", "test", "tags"]
User.expects(:tags_starting_with).returns(#tags).once
end
should "return JSON formatted tags array" do
tags = JSON::parse #response.body
assert_equal #tags, tags
end
end
end
Gemfile
gem "mocha"
If I run this test, I keep running into
- expected exactly once, not yet invoked: User.tags_starting_with(any_parameters)
If I use rails console test I can mock a class method just fine, and it works as expected.
I've been through this post and have done the Gemfile, require "false" bit. But to no avail, it just doesn't want to mock the class method of the User in the controller.
Other things I've tried, if I do User.tags_starting_with("bla") in the test itself, the expectation passes.
So any ideas on why the User in the controller isn't being mocked correctly?

As said on Twitter:
You're setting you your mock after you're doing your request :-)

Related

Rspec rails-controller-testing gem's assigns doesn't work

I'm using Rails 5.2 with rspec-rails 3.7 and rails-controller-testing gems.
I have a controller that filters results on the index action (yeah, bad practice, legacy code, etc). The problem is I need to test that when I do GET work_orders/index params: { keywords: 'some word' }, the instance variable #work_orders returns filtered results, but if I use assigns(:work_orders) it returns nil. I even tested this assigning the last WorkOrder to that variable, but it still doesn't show the variable in the hash.
work_orders_controller.rb
def index
... some code ...
#work_orders = WorkOrder.last
respond_to do |format|
format.html
end
end
spec/controllers/work_orders_controller_spec.rb
require 'rails_helper'
describe WorkOrdersController do
describe 'GET index' do
it 'collects work orders filtered by courier_ot in #work_orders' do
get :index
expect(assigns(:work_orders)).to be_an_instance_of WorkOrder
end
end
end
That is the simplest example I tested and it doesn't work. The assigns call returns nil, and if I use byebug to inspect the hash, it only has this: {"marked_for_same_origin_verification"=>true}.
The real test should use get :index, params: { keywords: 'asdf' } and test that it gets the filtered work orders.
Any help would be appreciated. Thanks.

Minitest - testing helper method that uses request params

I am porting an application that used RSpec successfully to test helper methods over to MiniTest. The following is the method in application_helper.rb
def active_controller?(controller_name)
params[:controller] == controller_name ? 'active' : nil
end
It is something that is used in views to help with active controller highlighting in controllers.
Below is the (not working) test for the helper.
require 'test_helper'
class ApplicationHelperTest < ActionView::TestCase
test 'active_controller? returns correct value' do
#request = ActionController::TestRequest.new
#request.params[:controller] = 'dashboard'
assert active_controller?('dashboard')
end
end
The problem is that I cant figure out how to access (or stub out) the request params to contain a controller to be able to assert the method returns true.
Any thoughts?
Try using the pry gem to see what #request, #request.params[:controller], and params[:controller] actually return.
The assertion active_controller?('dashboard') is checking 'dashboard' against params[:controller], which is currently nil, as you've only assigned #request.params[:controller] in your test.
If the test is:
test 'active_controller? returns correct value' do
params[:controller] = 'dashboard'
assert active_controller?('dashboard')
end
the assertion is comparing 'dashboard' to params[:controller], which has been assigned.

Mocking a non database backed model in Rails with Rspec

I am trying to create a non ActiveRecord model in my Ruby on Rails application according to http://railscasts.com/episodes/121-non-active-record-model. I am facing hard time testing it though.
I have following class in my app/models/sms.rb file.
class Sms
def initialize
# Do stuff here
end
def deliver
# Do some stuff here
end
end
I am unable to mock deliver method on my Sms class.
it 'should do someting' do
#sms = mock(Sms)
#sms.should_receive(:deliver).and_return(true)
post :create, :user_id => #user.id
flash[:notice].should == "SMS sent successfully."
response.should redirect_to(some_url)
end
In my controller code I do have a line that says #sms.deliver. Yet above gives following error:
Failure/Error: #sms.should_receive(:deliver).and_return(true)
(Mock Sms).deliver(any args)
expected: 1 time
received: 0 times
Any pointers?
Variables beginning with # are instance variables. The #sms your controller refers to is not the same #sms as your spec has defined.
Try changing
#sms = mock(Sms)
#sms.should_receive(:deliver).and_return(true)
to
Sms.any_instance.should_receive(:deliver).and_return(true)
If your version of RSpec doesn't have the any_instance method, you'll need to stub :new on the class:
#sms = mock(Sms)
Sms.stub(:new).and_return(#sms)
#sms.should_receive(:deliver).and_return(true)

RSpec mocking a nested model in Rails - ActionController problem

I am having a problem in RSpec when my mock object is asked for a URL by the ActionController. The URL is a Mock one and not a correct resource URL.
I am running RSpec 1.3.0 and Rails 2.3.5
Basically I have two models. Where a subject has many notes.
class Subject < ActiveRecord::Base
validates_presence_of :title
has_many :notes
end
class Note < ActiveRecord::Base
validates_presence_of :title
belongs_to :subject
end
My routes.rb file nests these two resources as such:
ActionController::Routing::Routes.draw do |map|
map.resources :subjects, :has_many => :notes
end
The NotesController.rb file looks like this:
class NotesController < ApplicationController
# POST /notes
# POST /notes.xml
def create
#subject = Subject.find(params[:subject_id])
#note = #subject.notes.create!(params[:note])
respond_to do |format|
format.html { redirect_to(#subject) }
end
end
end
Finally this is my RSpec spec which should simply post my mocked objects to the NotesController and be executed... which it does:
it "should create note and redirect to subject without javascript" do
# usual rails controller test setup here
subject = mock(Subject)
Subject.stub(:find).and_return(subject)
notes_proxy = mock('association proxy', { "create!" => Note.new })
subject.stub(:notes).and_return(notes_proxy)
post :create, :subject_id => subject, :note => { :title => 'note title', :body => 'note body' }
end
The problem is that when the RSpec post method is called.
The NotesController correctly handles the Mock Subject object, and create! the new Note object. However when the NoteController#Create method tries to redirect_to I get the following error:
NoMethodError in 'NotesController should create note and redirect to subject without javascript'
undefined method `spec_mocks_mock_url' for #<NotesController:0x1034495b8>
Now this is caused by a bit of Rails trickery that passes an ActiveRecord object (#subject, in our case, which isn't ActiveRecord but a Mock object), eventually to url_for who passes all the options to the Rails' Routing, which then determines the URL.
My question is how can I mock Subject so that the correct options are passed so that I my test passes.
I've tried passing in :controller => 'subjects' options but no joy.
Is there some other way of doing this?
Thanks...
Have a look at mock_model, which is added by rspec-rails to make it easier to mock ActiveRecord objects. According to the api docs:
mock_model: Creates a mock object instance for a model_class with common methods stubbed out.
I'm not sure if it takes care of url_for, but it's worth a try.
Update, 2018-06-05:
As of rspec 3:
mock_model and stub_model have been extracted into the rspec-activemodel-mocks gem.
In case zetetic's idea doesn't work out, you can always say Subject.new and then stub out to_param and whatever else you might need faked for your example.

RSpec: Expectation on model's not working while testing controller

I'm trying to write a functional test. My test looks as following:
describe PostsController do
it "should create a Post" do
Post.should_receive(:new).once
post :create, { :post => { :caption => "ThePost", :category => "MyCategory" } }
end
end
My PostsController (a part of it actually) looks as following:
PostController < ActiveRecord::Base
def create
#post = Post.new(params[:post])
end
end
Running the test I'm always receiving a failure, which says that the Post class expected :new but never got it. Still, the actual post is created.
I'm a newbie to RSpec. Am I missing something?
EDIT - Threw away the previous rubbish
This should do what you want
require File.dirname(__FILE__) + '/../spec_helper'
describe PostsController do
it "should create a Post" do
attributes = {"Category" => "MyCategory", "caption" => "ThePost"}
Post.stub!(:new).and_return(#post = mock_model(Post, :save => false))
Post.should_receive(:new).with( attributes ).and_return #post
post :create, { :post => attributes }
end
end
This assumes you are using rspecs own mocking library and that you have the rspec_rails gem installed.
You can use the controller method of Rspec-rails to test message expectations on controllers, as described here. So one way of testing your create action is like so:
describe PostsController do
it "should create a Post" do
controller.should_receive(:create).once
post :create, { :post => { :caption => "ThePost", :category => "MyCategory" } }
end
end
EDIT (making an argument)
You might want to consider whether it's a good idea to write a test that depends on the implementation of the create action. If you're testing for anything other than the proper responsibilities of a controller, you run the risk of breaking tests when refactoring, and having to go back and rewrites tests when the implementation changes.
The job of the create action is to create something -- so test for that:
Post.count.should == 1
and then you know whether a Post was created, without depending on how it was created.
EDIT #2 (um...)
I see from your original question that you already know the Post is being created. I'd still argue that you should test for behavior, not implementation, and that checking for whether the model receives a message is not a good thing in a controller test. Maybe what you're doing is debugging, not testing?

Resources