How to test after_create method in rspec? - ruby-on-rails-3

I have after_create method as follow:
company.rb
class Company < ActiveRecord::Base
after_create :create_subscriptions
def create_subscriptions
subscription=Subscription.create(:company_id => self.id, :subscription_dt => Date.today, :is_active => 'Y', :last_renewal_dt => Date.today + 2,:user_id => self.users.first.id)
subscription.save
end
end
While i create a company after_create method called and enter data in subscription table.
In rspec I created company and it success fully created. But how do test "create_subscriptions" method? whoch call in after create. Can i do query in rspec code? like
rspec code:
#company = create(:company)
#sub = Subscription.find(:first,:conditions => ['company_id= ?', #company.id] )
expect(#sub.company_id).should eq(#company.id)
is it ok?? I did not see this type of query in rspec code in my google searching. Have use stub or mock in this?
can anyone please guide me? I think i have to use stub and mock but i don't know how to use them?
Thanks,

Your idea is correct but your code looks "obsolete" (for Rails 2.x)
I can suggest the following variant
#company = create(:company)
#company.reload
expect(#company.subscriptions).not_to be_empty
# additionally you can test attributes
subscription = #company.subscriptions.first
expect(subscription.is_active).to eq('Y')
PS it is necessary to add has_many :subscriptions to Company and belongs_to to Subscription

Related

Rails How To Manually Create Child Object Without Form

I was wondering what the best implementation would be to programatically generate a single child object for a parent(s) without the use of a form.
In my case I have an existing forum system that I would like to tie into my Upload system via comments. I would like to manually create a child Forum object to discuss said upload in the same create action that the Upload is created. The two models have a relationship as so:
Child forum:
class Forum < ActiveRecord::Base
...
belongs_to :upload
end
Parent upload:
class Upload < ActiveRecord::Base
...
has_one :forum
end
I was thinking of something along the lines of:
class UploadsController < ApplicationController
...
def create
#Create the upload and a new forum + initial post to discuss the upload
#upload = Upload.new(params[:upload])
#forum = Forum.new(:upload_id => #upload.id, :title => #upload.name...)
#first_post = Post.new(:forum_id => #forum.id....)
if #upload.save && #topic.save && #first_post.save
redirect_to :action => "show", :id => #upload.id
else
redirect_to :action => "new"
end
end
end
Which is fairly close to what I wanted to do but the parent ids aren't generated until the parent objects are saved. I could probably do something like:
#upload.save
#forum = Forum.new(:upload_id => #upload.id...
#forum.save....
But I thought it might be cleaner to only persist the objects if they all validated. I'm not sure, does anybody else know of a better implementation?
I would recommend moving the forum creation from the controller to the model. The forum will only be created on the successful creation of the Upload.
class Upload < ActiveRecord::Base
...
has_one :forum
after_create :create_forum
...
def create_forum
Forum.create(:upload_id => self.id, :title => self.name...)
end
end
class Forum < ActiveRecord::Base
...
has_many :posts
after_create :create_first_post
...
def create_first_post
Post.new(:forum_id => self.id)
# or self.posts << Post.new()
end
end

How to invoke a rails sweeper in this scenario?

As you can see from code below. I am caching the show action. I also have the following method in the show action View.create_for(#song).
I would like to make it so, when View.create_for(#song) is called, it clears out the respective cache.
How would I go about this? Do I have to manually invoke the rails sweeper in the View model? If so, how?
My controller:
class SongsController < ApplicationController
caches_action :show, :cache_path => (proc do
song_path(params[:id], :user_id => user_signed_in? ? current_user.id : nil)
end)
# I tried with the following line, but this is not what I want. I only want to call the sweeper when `View.create_for(#song)` is called:
# cache_sweeper :views_sweeper, :only => [:show]
def show
#song = Song.find(params[:id])
View.create_for(#song)
end
end
My Sweeper:
class SongsSweeper < ActionController::Caching::Sweeper
observe Song
def after_save(song)
expire_fragment(/songs\/#{song.id}\/.*?/)
end
end
I think you should be referring to the songs_sweeper, not the views_sweeper:
cache_sweeper :songs_sweeper, :only => [:show]
I'm not sure of your requirements, but you could also be more specific in your SongsSweeper by changing after_save to after_create:
class SongsSweeper < ActionController::Caching::Sweeper
observe Song
def after_create(song)
expire_fragment(/songs\/#{song.id}\/.*?/)
end
end

TDD with RSpec and Rails: testing controller actions with no view

As I continue to learn my way around TDD with RSpec 2 and Rails 3.1, I can't seem to find a solution to this problem.
I have a Users controller with a new and create action. In my UsersController spec, I have
users_controller_spec.rb
describe "POST 'create'" do
before(:each) do
#attr = Factory.attributes_for(:user)
end
it "should assign an #user variable" do
post :create, :user => #attr
assigns[:user].should_not be_nil
assigns[:user].should be_kind_of(User)
end
end
and in my UsersController,
users_controller.rb
def create
#user = User.new(params[:user])
end
This spec is failing with
1) UsersController POST 'create' should assign an #user variable
Failure/Error: post :create, :user => #attr
ActionView::MissingTemplate:
I can continue to implement application code to get this test to pass, but I feel like this test should be passing as it is.
Any suggestions?
Your create method needs to do something. Either render a template or redirect. Since you're not telling it to redirect it's assuming that you want it to render a template but when it can't find a create.html.erb file it throws an error.
You're best bet is to do either this:
def create
#user = User.new(params[:user])
redirect_to root_url
end
or this:
def create
#user = User.new(params[:user])
render :nothing => true
end
To test rendering nothing you'll want:
expect(response).to render_template(nil)
I've come across this recently myself. It seems one possibility would be to rescue the error in your test.
it "should assign an #user variable" do
begin
post :create, :user => #attr
rescue ActionView::MissingTemplate
# This is okay because(/as long as) we test the render/redirect
# in a separate spec where we don't rescue this exception.
end
assigns[:user].should_not be_nil
assigns[:user].should be_kind_of(User)
end
I don't know how "correct" this solution is. On one hand, it definitely emphasizes the "testing one thing at a time" mentality, on the other, it seems kind of ugly.
Edit
I suppose you could make some nice wrappers in your spec helper, something like
def post?(action, params = {})
post action, params
rescue ActionView::MissingTemplate
end

Rails3 ActiveResource Post Call delivers an empty Parameter Hash

I am trying to create a new "Person" in a Sinatra API app from a Rails3 app using ActiveResource and Json. In Rails3, I created a "Person" model and using ActiveResource I correctly call the API, which correctly reads the URL, but no parameters seem to get passed with the object.
From Rails3 Person Model:
class Person < ActiveResource::Base
self.site = "http://127.0.0.1:9393/"
self.collection_name = "person/add"
self.format = :json
end
From the Rails3 console:
u=Person.new({"last_name"=>"Bill", "first_name"=>"Smith"})
=> #<Person:0xb73176f0 #attributes={"last_name"=>"Bill", "first_name"=>"Smith"}, #prefix_options={}>
puts u.attributes
=> last_nameBillfirst_nameSmith
u.save
=> True
From the Sinatra app:
puts #app.params.keys
=> Nil
puts #app.params['last_name']
=> Nil
puts #app.params[:last_name]
=> Nil
Using the IRB Console this works:
Net::HTTP.post_form(URI.parse('http://127.0.0.1:9393/user/add.json'),{'first_name' => 'Smith', 'last_name' => 'Bill'})
Can someone please give some direction as to what I missed or am doing wrong thank you.
Person object should know attributes, as you did on console. When doing Person.find, it gets attrs via activeresource, but Person.new doesn't know them so that any-way to tell to Person is required at Person.new like the following:
class PeopleController < ApplicationController
...
def new
#person = Person.new(:name=>nil, :age=>nil, ...)
end
...
Does this answer?

Routes in Rails3 - Controller and routes for 2 post functions

I'm trying to write an app in rails 3 and I'm having some trouble figuring out the routes and controllers for a test that I want the user to take. The basic requirements for this app are:
Users, Tests and Questions are all in separate models.
A User has_many Tests. A Test has_many Questions
Provide a link on the user_profile page to /test/new to create the test record.
Provide a link on /test/new to /test/:id/part1 (where :id is the test_id) so that the user can complete the first part of the test. Questions will be retrieved from the db and presented on this page.
Provide a link on /test/:id/part1 to /test/:id/part2 so that the user can complete the second part of the test. Again, questions are retrieved from the db.
Provide a link on /test/:id/part2 to submit the test and return to the user's profile.
I've completed the models, which even pass their tests, so I think I have finished parts 1 and 2.
user.rb
Class User < ActiveRecord::Base
has_many :tests
end
test.rb
Class Test < ActiveRecord::Base
belongs_to :user
has_many :questions
end
question.rb
Class Question < ActiveRecrod::Base
belongs_to :test
end
My issues start when I try to put these models together using routes and controllers.
routes.rb
resources :users
resources :tests do
member do
post 'part1'
post 'part2'
end
end
users/show.html.erb
<%= link_to "Start The Test", new_test_path %>
tests/new.html.erb
<%= link_to "Part 1", part1_test_path(#test) %>
tests_controler.rb
class TestsController < ApplicationController
def new
#test = Test.new(current_user)
end
def part1
# still just a stub
end
end
I'm getting this error when I click on the link to take Part 1 of the test:
No route matches {:action=>"part1", :controller=>"tests", :id=>#<Test id: nil, taken_at: nil, user_id: nil, created_at: nil, updated_at: nil>}
Any help on this would be greatly appreciated.
By defining a member of the routes it's expecting an existent test, ie. one which is saved and has an id.
e.g.
part1_test_path = /test/123/part1
What you need is a collection route.
resources :tests do
collection do
post 'part1'
end
member do
post 'part2'
end
end
e.g.
part1_test_path = /test/part1
edit
Suggested solution:
resources :test, :path_names => { :new => 'part_1', :edit => 'part_2' } *1
def new
#test = Test.new
#new view
form_for #test do
...
def create
#test = Test.new params[:test]
if #test.save
redirect_to edit_test_path #test
def edit
#test = Test.find params[:id]
#edit view
form_for #test do
def update
#test = Test.find params[:id]
if #test.update_attributes params[:test]
redirect_to test_path #test
def show # test results
#test = Test.find params[:id]
if #test.incomplete *2
redirect_to edit_test_path #test
*1 See rails guide on routing. This will give you urls like this
test/part1
test/123/part2
You should put all of your validation in the model; your requirements of test data. Conditional validation will be required, depending on whether it's a new_record? or not ie if you're at part 1 or 2.
*2
add a method to your model which checks test completeness.
def incomplete
self.some_test_field.blank?
Let me know if you don't understand anything.