Ruby on Rails RSpec Compare Function Values - ruby-on-rails

I have two function values that I'm trying to compare and make sure one is greater than the other, and I just cannot figure out how to do it in RSpec. One function is "uncompleted_tasks" and the other is "tasks.count", both of which are part of a User model. Here is what I have in RSpec. The subject is an instance of the User model and RSpec gives me the error, "undefined local variable or method 'ut' for # (NameError)", on the line "expect(ut).should be <= tc". What's going on?
describe "uncompleted tasks should be less than or equal to total task count" do
before do
ut = subject.uncompleted_tasks
tc = subject.tasks.count
end
expect(ut).should be <= tc
end

Check out this SO answer for further details, but basically local variables in RSpec are limited to their local scope, including before blocks. So, the variables defined in your before block aren't available in the test. I'd suggest using instance variables for this:
describe "uncompleted tasks" do
before do
#ut = subject.uncompleted_task
#tc = subject.tasks.count
end
it "should be less than or equal to total task count" do
expect(#ut).should be <= #tc
end
end

You need to use instance variables and your expect needs to be inside an it block. Like below:
describe "uncompleted tasks should be less than or equal to total task count" do
before do
#ut = subject.uncompleted_tasks
#tc = subject.tasks.count
end
it "something" do
expect(#ut).should be <= #tc
end
end

Related

NoMethodError when running RSpec test

I am getting the error NoMethodError when trying to use RSpec for testing the gem I created.
this is my gem:
~/project/gemz/spec/lib/checksomething.rb
class Checkpercentage
#1) what is x% of y?
def self.findAmount(rate, base)
resultAmount = rate * base/100
return resultAmount
end
#2) x is what percentage of y?
def self.findPercent(amount, base)
resultPercent = amount/base * 100
return resultPercent
end
#3) x is y% of what number?
def self.findBase(amount, rate)
resultBase = amount/rate *100
return resultBase
end
end # End Module
And this is my rspec test file:
./project/gemz/spec/lib/checkpercentage_spc.rb
require "spec_helper"
require "checksomething"
RSpec.describe Checkpercentage, '#intNum' do
context "with no integer entered" do
it "must be a number" do
checkpercentage = Checkpercentage.new
checkpercentage.findPercent(100, 200)
expect(checkpercentage.intNum) > 0
end
end
end
I want to test if the values enterend in the findPercentage method are > 0. However, when I run the rspec command in my terminal (rspec spec/lib/checkpercentage_spc.rb) the following error comes up:
Failures:
1) Checkpercentage#intNum with no integer entered must be a number
Failure/Error: checkpercentage.findPercent(100, 200)
NoMethodError: undefined method `findPercent' for #<Checkpercentage:0x9956ca4>
# ./spec/lib/checkpercentage_spc.rb:8:in `block (3 levels) in <top (required)>'
Finished in 0.00089 seconds (files took 0.17697 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./spec/lib/checkpercentage_spc.rb:6 # Checkpercentage#intNum with no integer entered must be a number
I'm fairly new at ruby on rails. Can anyone point me to the right direction? Any help is appreciated.
A couple of things:
As Santhosh wrote, the way you have declared your methods (using self) makes all of them methods for the Class (Checkpercentage) itself. If you want them to be called on instance (Checkpercentage.new) you have to remove self from the declaration.
What is intNum (looks like Java but it doesn't exist in Ruby)? If I understand correct you want to check that findPercent(amount, base) returns a positive number. In that case, the right RSpec syntax is expect(checkpercentage.findPercent(100, 200)).to be > 0.
In Ruby a) camelCase is avoided in favour of camel_case and b) methods return the result of the last line that was executed. These mean you can rewrite your code as follows:
class Checkpercentage
#1) what is x% of y?
def find_amount(rate, base)
rate * base/100
end
#2) x is what percentage of y?
def find_percent(amount, base)
amount/base * 100
end
#3) x is y% of what number?
def find_base(amount, rate)
amount/rate * 100
end
end # end Module
notice that I have removed the self keywords to show you what I meant with my first point - now the syntax in the test you have written (checkpercentage.method_name) will be correct
Notice, furthermore, that there is a bug in your code - it works but not as you want it too. Hopefully the tests you'll write will help you find it and fix it, if not let us know!

testing behaviour of method that scopes outputs into a hash

I have a method which creates a key value pair of delivery costs, the key being the type, and the value being the cost.
def calculate_scoped_job_delivery_costs
delivery_hash = {}
['install', 'fuel', 'breakdown'].each do |scope|
delivery_hash[scope.humanize] = job_delivery_costs.send(scope).inject(0) { |total, item| total + (item.cost_per_unit * item.hour_count) * item.quantity }
end
delivery_hash.delete_if {|key, value| value <= 0 }
end
the key is a scope in the job delivery costs model, which retrieves all associated costs with that scope and adds them up. It works, but I want to test its behaviour, albeit retrospectively.
So its core expected behaviour is:
it should output a hash
it should calculate each scope value
it should remove blank values from the hash
So I have written this test (factories posted below)
let(:jdc1){FactoryGirl.create :job_delivery_cost, job: job, delivery_cost: delivery_cost}
let(:jdc2){FactoryGirl.create :job_delivery_cost, job: job, delivery_cost: delivery_cost}
let(:jdc3){FactoryGirl.create :job_delivery_cost, job: job, delivery_cost: delivery_cost}
describe "calculate_scoped_job_delivery_costs" do
before do
allow(jdc1).to receive(:timing).and_return('fuel')
jdc2.update_attributes(quantity: 4)
jdc2.delivery_cost.update_attributes(timing: 'breakdown')
allow(job).to receive(:job_delivery_costs).and_return(JobDeliveryCost.where(id: [jdc1,jdc2,jdc3].map{|jdc| jdc.id}))
end
it "should retrieve a hash with jdc scopes" do
expect(job.calculate_scoped_job_delivery_costs.is_a?(Hash)).to be_truthy
end
it "should calculate each hash value" do
expect(job.calculate_scoped_job_delivery_costs).to eq "Fuel"=>15.0
end
it "should remove blank values from hash" do
expect(job.calculate_scoped_job_delivery_costs).to_not include "Breakdown"=>0
end
end
So in the last test, it passes, why? I have purposefully tried to make it break by updating the attributes in the before block on jdc2 so that breakdown is another scoped value.
Secondly, by changing the state of jdc2 and its values, this should break test 2 as fuel is no longer calculated against the same values.
Here are my factories...
FactoryGirl.define do
factory :job_delivery_cost do
job
delivery_cost
cost_per_unit 1.5
quantity 3
hour_count 1.0
end
end
FactoryGirl.define do
factory :delivery_cost do
title
timing "Fuel"
cost_per_unit 1.5
end
end
FactoryGirl.define do
factory :job do
job_type
initial_contact_id_placeholder {FactoryGirl.create(:contact).id}
title "random Title"
start "2013-10-04 11:21:24"
finish "2013-10-05 11:21:24"
delivery "2013-10-04 11:21:24"
collection "2013-10-05 11:21:24"
delivery_required false
collection_required false
client { Client.first || FactoryGirl.create(:client) }
workflow_state "offer"
admin
end
end
job has_many :job_delivery_costs.
job_delivery_cost belongs_to :delivery_cost
has_many :job_delivery_costs
has_many :jobs, through: :job_delivery_costs
I am really struggling with the logic of these tests, I am sure there are more holes than what I have laid out above. I welcome criticism in that regard.
thanks
A couple of suggestions:
Remember that let is lazy-evaluated; factories within the block are not created until the symbol defined by let is encountered in the code. This can have unexpected consequences for things like scopes, where you might think that the database already includes your factory-generated rows. You can get around this by using let!, which is evaluated immediately, or by restructuring the spec to ensure things get created in the right order.
I prefer not to do partial stubbing where it can be avoided. For scopes, you are probably better off using factories and just letting the scopes retrieve the rows instead of stubbing the relations. In your case this means getting rid of the code in the before block and setting up each example with factories, so that the scopes are retrieving the expected values.

rspec should_receive is not working but expect is working

I have class like below
#bank.rb
class Bank
def transfer(customer1, customer2, amount_to_transfer)
if customer1.my_money >= amount_to_transfer
customer1.my_money -= amount_to_transfer
customer2.my_money += amount_to_transfer
else
return "Insufficient funds"
end
end
end
class Customer
attr_accessor :my_money
def initialize(amount)
self.my_money = amount
end
end
And my spec file looks as below:
#spec/bank_spec.rb
require './spec/spec_helper'
require './bank'
describe Bank do
context "#transfer" do
it "should return insufficient balance if transferred amount is greater than balance" do
customer1 = Customer.new(500)
customer2 = Customer.new(0)
customer1.stub(:my_money).and_return(1000)
customer2.stub(:my_money).and_return(0)
expect(Bank.new.transfer(customer1, customer2, 2000)).to eq("Insufficient funds")
expect(customer1).to have_received(:my_money) # This works
customer1.should_receive(:my_money) #throws error
end
end
end
As per https://relishapp.com/rspec/rspec-mocks/v/2-14/docs/message-expectations both expect and should_receive are same but expect is more readable than should_receive. But why it is failing? Thanks in advance.
place this line:
customer1.should_receive(:my_money)
before
expect(Bank.new.transfer(customer1, customer2, 2000)).to eq("Insufficient funds")
expect to have_received and should_receive have diffent meaning
expect to have_received passes if object already received expected method call while
should_receive passes only if object will receive expected method call in future (in scope of current testcase)
if you would write
expect(customer1).to receive(:my_money)
instead of
expect(customer1).to have_received(:my_money)
it would fail too. Unless you place it before the line which calls this method.

Ruby: Default values for define?

I have a question about define my main issue is I am a bit confused on how the parameters work for it.
This is my Methods
def repeat(repeated_word)
#repeated_word = repeated_word
"##repeated_word ##repeated_word"
end
This is my rspec test to make sure my method works.
describe "repeat" do
it "should repeat" do
repeat("hello").should == "hello hello"
end
# Wait a second! How can you make the "repeat" method
# take one *or* two arguments?
#
# Hint: *default values*
it "should repeat a number of times" do
repeat("hello", 3).should == "hello hello hello"
end
end
it passes the first test but fails the second. My confusion is if i add a second parameter meaning def repeat(repeat_word, times_repeated)
the first test then fails because it has the wrong number of arguments. Not sure how to set up default values?
def repeat(repeated_word, repeats=2)
repeats.times.map { repeated_word }.join(' ')
end

How to test the number of database calls in Rails

I am creating a REST API in rails. I'm using RSpec. I'd like to minimize the number of database calls, so I would like to add an automatic test that verifies the number of database calls being executed as part of a certain action.
Is there a simple way to add that to my test?
What I'm looking for is some way to monitor/record the calls that are being made to the database as a result of a single API call.
If this can't be done with RSpec but can be done with some other testing tool, that's also great.
The easiest thing in Rails 3 is probably to hook into the notifications api.
This subscriber
class SqlCounter< ActiveSupport::LogSubscriber
def self.count= value
Thread.current['query_count'] = value
end
def self.count
Thread.current['query_count'] || 0
end
def self.reset_count
result, self.count = self.count, 0
result
end
def sql(event)
self.class.count += 1
puts "logged #{event.payload[:sql]}"
end
end
SqlCounter.attach_to :active_record
will print every executed sql statement to the console and count them. You could then write specs such as
expect do
# do stuff
end.to change(SqlCounter, :count).by(2)
You'll probably want to filter out some statements, such as ones starting/committing transactions or the ones active record emits to determine the structures of tables.
You may be interested in using explain. But that won't be automatic. You will need to analyse each action manually. But maybe that is a good thing, since the important thing is not the number of db calls, but their nature. For example: Are they using indexes?
Check this:
http://weblog.rubyonrails.org/2011/12/6/what-s-new-in-edge-rails-explain/
Use the db-query-matchers gem.
expect { subject.make_one_query }.to make_database_queries(count: 1)
Fredrick's answer worked great for me, but in my case, I also wanted to know the number of calls for each ActiveRecord class individually. I made some modifications and ended up with this in case it's useful for others.
class SqlCounter< ActiveSupport::LogSubscriber
# Returns the number of database "Loads" for a given ActiveRecord class.
def self.count(clazz)
name = clazz.name + ' Load'
Thread.current['log'] ||= {}
Thread.current['log'][name] || 0
end
# Returns a list of ActiveRecord classes that were counted.
def self.counted_classes
log = Thread.current['log']
loads = log.keys.select {|key| key =~ /Load$/ }
loads.map { |key| Object.const_get(key.split.first) }
end
def self.reset_count
Thread.current['log'] = {}
end
def sql(event)
name = event.payload[:name]
Thread.current['log'] ||= {}
Thread.current['log'][name] ||= 0
Thread.current['log'][name] += 1
end
end
SqlCounter.attach_to :active_record
expect do
# do stuff
end.to change(SqlCounter, :count).by(2)

Resources