So I am trying to ensure that a callback happens on save... My test is:
user = create_user
login user
visit new_post_path
fill_in "post_title", :with => "my post"
user.expects(:publish_post!).once
click_button "post_submit"
and I get:
1) Failure:
test: Post should place a post. (PostTest)
[test/integration/post_test.rb:72:in __bind_1311708168_640179'
/test/test_helper.rb:37:inexpectation_on'
test/integration/post_test.rb:70:in `__bind_1311708168_640179']:
not all expectations were satisfied
unsatisfied expectations:
- expected exactly once, not yet invoked: #.publish_post!(any_parameters)
satisfied expectations:
- allowed any number of times, not yet invoked: Post(id: integer, title: string, method_of_exchange: string, you_tube_url: string, lat: decimal, lng: decimal, bounty: integer, distance: integer, user_id: integer, consider_similar_offers: boolean, description: text, created_at: datetime, updated_at: datetime, address: string, city: string, state: string, zip: string, country: string, county: string, category_id: integer, slug: string, status: string, popularity: integer, delta: boolean, share_count: integer, needs_reply: decimal, needs_offer: decimal, district: string).facets(any_parameters)
- allowed any number of times, not yet invoked: {}.for(any_parameters)
- expected never, not yet invoked: #.publish_post!(any_parameters)
Yet my post model does:
class Post < ActiveRecord::Base
belongs_to :user
after_create :publish
def publish
user.publish_post!
end
end
and my posts controller's create action does indeed assign the user the the post...
class PostsController < ApplicationController
def create
post = Post.new(params[:post])
post.user = current_user
post.save
end
end
...
The functionality works fine when testing manually.. So I don't get why this is not working when automated?
I really wouldn't try to use mocking expectations in an integration test. That kind of testing belongs in low-level unit tests for the Post class itself. Try to think about integration tests as just looking at the system from the outside, as a user would. What effect does the callback have? Can you just check for the outcome of that effect?
Related
I'm trying to write a test to see if my Hospital model is receiving a custom method look_for_hospitals.
Here is the test:
Rspec.describe HospitalsController, type: :controller do
describe '#search' do
it 'should call the model method to do the search' do
# model should implement method look_for_hospitals
expect(Hospital).to receive(:look_for_hospitals).with('keyword')
# form in search page must be named 'keywords'
get :search, params: {:keywords => 'keyword'}
expect(assigns(:hospitals))
end
end
Here is my model:
class Hospital<ApplicationRecord
def self.look_for_hospitals term
end
end
And here is the method search in the HospitalsController:
def search
keyword = params[:keywords]
#hospitals = Hospital.look_for_hospitals(keyword)
end
When I run my test this is the error I'm having:
1) HospitalsController#search should call the model method to do the search
Failure/Error: expect(Hospital).to receive(:look_for_hospitals).with('keyword')
(Hospital(id: integer, cnes: string, number: integer, address: text, latitude: string, longitude: string, name: string, phones: text, nature: string, specialties: text, rpa: string, microregion: string, created_at: datetime, updated_at: datetime) (class)).look_for_hospitals("keyword")
expected: 1 time with arguments: ("keyword")
received: 0 times
I know there is pretty much nothing implemented yet, but i'm trying the tdd approach of writing the tests first and then the methods.
Sorry if my english is a bit strange, not native english speaker.
Thanks!
As Tom Lord pointed out, the problem was in the before_action line at the begining of the Controller. I just needed to stub those and the problem was gone.
After upgrading CanCan 1.4 to CanCanCan 2.0, I can not anymore cache current_ability into memcached with Dalli.
I got error below :
Cache write: profils/11-20170821121414000000::ability
Marshalling error for key 'development:profils/11-20170821121414000000::ability': can't dump hash with default proc
You are trying to cache a Ruby object which cannot be serialized to memcached.
/usr/local/rvm/gems/ruby-2.4.0#rails5.1.1/gems/dalli-2.7.6/lib/dalli/server.rb:414:in `dump'
I store current_ability in memcached through Dalli by overriding CanCan.Ability::current_ability in app/controllers/application_controller.rb
def current_ability
Rails.cache.fetch("#{current_user.profil.cache_key}::ability") do
super
end
end
Why I store current_ability in cache ?
cancan slowing down page loads, Because I have a lot of custom actions in many controllers, each page reload all abilities...
Any pointers in regards to cacheing / optimizing / best practices would be most appreciated. Thanks!
update 1
Add app/models/ability.rb
class Ability
include CanCan::Ability
def initialize(user)
alias_action :edit, :to => :read
role = user.profil
if role.label == 'administrateur'
can :manage, :all
else
role.rights.each do |right|
model = right.resource.constantize rescue nil
next unless model
[:read, :create, :update, :destroy].each do |capability|
can capability, model if right.send("#{capability}?")
end
end
# Custom actions
can([:collective, :collective_week, :presence, :day_details, :day_details_json], Movement) if can?(:read, Movement)
can(:quick_add, Movement) if can?(:create, Movement)
can([:actions, :set_action, :choose, :previous, :families_addresses, :ios,:families_members,:potential_relationships,:list_families,:list_decisions], Person) if can?(:read, Person)
can(:addresses, Family) if can?(:read, Family)
...
...
...
end
end
end
Update 2
Print an object of Ability (below is just a snippet of ability but the stucture is the same) That's the data that I try to store in memcached :
#<Ability:0x000000093030a0
#aliased_actions={:read=>[:index, :show, :edit], :create=>[:new], :update=>[:edit]},
#expanded_actions=
{[:index, :show, :edit]=>[:index, :show, :edit]},
#rules=
[#<CanCan::Rule:0x00000008fb3e18
#actions=[:read],
#base_behavior=true,
#block=nil,
#conditions={},
#match_all=false,
#subjects=[AcceptanceReason(id: integer, label: string, created_at: datetime, updated_at: datetime, institution_position: integer, acceptance_number: integer, department_type_id: integer)]>,
#<CanCan::Rule:0x00000008fb3760
#actions=[:create],
#base_behavior=true,
#block=nil,
#conditions={},
#match_all=false,
#subjects=[AcceptanceReason(id: integer, label: string, created_at: datetime, updated_at: datetime, institution_position: integer, acceptance_number: integer, department_type_id: integer)]>],
#rules_index=
{AcceptanceReason(id: integer, label: string, created_at: datetime, updated_at: datetime, institution_position: integer, acceptance_number: integer, department_type_id: integer)=>[0, 1, 2, 3],
Activity(id: integer, created_at: datetime, updated_at: datetime, institution_id: integer, label: string, activity_type_id: integer, employee_id: integer, department_id: integer, group_id: integer, beginning_at: date, ending_at: date, location: text, comments: text, objectif: text, evaluation: text, colour: string, collective_intervention_mean_id: integer, collective_intervention_status_id: integer, archive: boolean, activity_beginning_time: time, activity_ending_time: time, moyens_materiels: text, transports: text, autres_intervenants: text, budget: text, frequency: string)=>
[4, 5, 6, 7, 645],
ActivityCode(id: integer, label: string, created_at: datetime, updated_at: datetime)=>[8, 9, 10]}
So I have a Request model (I know it's a terrible name), and 2 single inherited models TenantRequest and PropertyRequest. Now I have fixtures for all 3. So I wrote functional controller tests for my requests_controller and my tenant_requests_controller, which both work fine. But for some reason, my property_controller tests show me the following error for every setup:
1) Error:
PropertyRequestsControllerTest#test_should_get_edit:
ActiveRecord::RecordNotFound: Couldn't find Request with 'id'=298486374
test/controllers/property_requests_controller_test.rb:12:in `block in <class:PropertyRequestsControllerTest>'
This is the tenant_requests.yml:
one:
title: This is the title of the tenant request
body: This is the body
user: regular
email: jim#retail.com
type: TenantRequest
contact_name: Jim
Here is my property_request.yml:
one:
title: This is the title of the property request
body: This is the body for property
user: broker
email: sue#broker.com
type: PropertyRequest
contact_name: Sue
budget: 1234
city: New York
region: Manhattan
created_at: now
updated_at: now
status: open
company: Walmart
contact_position: Boss
contact_phone: 555-555-5555
squarefeet: 12345
broker: true
parking: true
onsite_tour: true
part_of_town: Downtown
time_to_reach: 7pm
budget: 1234
Here is the property_requests_controller_test:
require 'test_helper'
class PropertyRequestsControllerTest < ActionController::TestCase
setup do
#regular = users(:jim)
#broker = users(:sue)
#analyst = users(:kev)
#admin = users(:lin)
sign_in :user, #analyst
#myrequest = property_requests(:one)
end
test "should get index" do
get :index
assert_response :success
assert_not_nil assigns(:requests)
end
test "should get new" do
get :new
assert_response :success
end
test "should create request successfully" do
assert_difference('Request.count') do
post :create, request: { contact_name: 'Sue', body: 'this is the body', email: 'sue#broker.com', title: 'newly created property request', type: 'PropertyRequest' }
end
assert_redirected_to property_request_path(PropertyRequest.last)
end
If you need more information, please let me know and I can add it. Thank you,
According to this blog post, fixture files have a one-to-one relationship with database tables. There could be a conflict occurring from having files for each child class. Try placing all fixtures into requests.yml.
Your fixture file is named property_request.yml (singular), while you're calling property_requests(:one) (plural) in the test itself. The Rails Guides show fixture files given pluralized names, so I would rename the file to property_requests.yml to make them match and conform to Rails' conventions (and hope that's the issue).
I am currrently working on a project that has to implement dynamic workflow.
Dynamic: I store workflow's states in database table called wf_steps and the workflow gem has to create states for a particular workflow from the database
For that I am trying to use the workflow gem. You can see how it initializes states and corresponding events in the gem's github-page.
My code:
class SpsWorkflow < ActiveRecord::Base
belongs_to :user
has_many :wf_steps
include Workflow
workflow do
# binding.pry
# read wf-states from the database
# for now let event be same for all the states
self.wf_steps.each do |step|
state step.title.to_sym do
event :assign, transitions_to: :assigning
event :hire, transitions_to: :hiring
event :not_hire, transitions_to: :not_hiring
end
end
end
end
Expectation and Problem encountered:
I expected in the code block below the term self.wf_steps would return my SpsWorkflow's instance/collection. However the self keyword returns #<Workflow::Specification:0x000000063e23e8 #meta={}, #states={}> when I use binding.pry inside the workflow method's block ( I commented in the code )
# read wf-states from the database
# for now let event be same for all the states
self.wf_steps.each do |step|
state step.title.to_sym do
Need you help, thanks
EDIT:
I also tried storing the instance in a variable and using the variable inside the block passing to the workflow method call.
class SpsWorkflow < ActiveRecord::Base
include Workflow
sps_instance = self
But I got the instance of the class SpsWorkflow like
SpsWorkflow(id: integer, workflow_state: string, assigned_to: integer, title: string, description: string, organization_id: integer, user_id: integer, created_at: datetime, updated_at: datetime)
but I want
#<SpsWorkflow id: 1, workflow_state: "step1", assigned_to: nil, title: "Software Engineer", description: "Hire best engineer", organization_id: nil, user_id: 1, created_at: "2015-08-08 00:58:12", updated_at: "2015-08-08 00:58:12">
You have used:
workflow do
self.something
end
self in the context of this block will refer to the WorkflowSpecification. If you really want access to the instance of SpsWorkflow, you may have to pass it into the block or assign it to a different variable and use it there.
I finally solved it using a activerecord callback
class SpsWorkflow < ActiveRecord::Base
include Workflow
after_initialize do
sps_instance = self
SpsWorkflow.workflow do
# read wf-states as well as events from the database
sps_instance.wf_steps.each do |step|
state step.title.to_sym do
event :assign, transitions_to: :step2
event :hire, transitions_to: :hire
event :not_hire, transitions_to: :not_hiring
end
end
end
end
belongs_to :user
has_many :wf_steps
end
I'm having trouble getting rspec to properly test an Active Record callback. In my model I have:
after_create :check_twitter_limit, if: Proc.new { |row| row.service == "twitter" }
Where service is the social media site being saved to the db. In essence, if the event is a twitter event, let's check the rate limit.
The method looks like this:
def check_twitter_limit
binding.pry
if EventTracker.within_fifteen_minutes.twitter.size > 12 && EventTracker.within_fifteen_minutes.twitter.size % 3 == 0
alert_notifier.ping("*[#{user}](#{ANALYTICS_URL}#{distinct_id})* (#{organization}) just _#{event}_ and has made *#{EventTracker.within_fifteen_minutes.twitter.size}* twitter requests in the last 15 minutes!")
end
end
Notice the pry binding. I have this in here as proof that the code works. Every time I run rspec, I get into the pry binding, so I know the method is being called.
Here is the spec:
it "should trigger the twitter alert limit when conditions are met" do
expect(EventTracker).to receive(:check_twitter_limit)
EventTracker.create({
event: "Added twitter users to list",
organization: "Org",
user: "Cool Guy",
event_time_stamp: Time.now.to_i - 14400,
distinct_id: "1",
media_link: "http://www.twitter.com",
is_limited: true,
service: "twitter"
})
end
Here is the error:
Failure/Error: expect(EventTracker).to receive(:check_twitter_limit)
(<EventTracker(id: integer, event: string, organization: string, user: string, event_time_stamp: integer, distinct_id: string, media_link: string, is_limited: boolean, service: string, created_at: datetime, updated_at: datetime) (class)>).check_twitter_limit(any args)
expected: 1 time with any arguments
received: 0 times with any arguments
This test fails despite the fact that I know the callback is being triggered when using pry, and I can confirm that the record is being saved to the db.
Any ideas on what is going on?
I think you want an instance of EventTracker to receive check_twitter_limit, not the class itself. You can do this in RSpec 3.0 with
expect_any_instance_of(EventTracker).to receive(:check_twitter_limit)