How to use helper methods in RSpec? - ruby-on-rails

I have a helper method defined in my RSpec spec which creates an instance of the class Dog. But the spec is not able to recognize the method call dog.good_dog.
helpers_demo_spec.rb
class Dog
attr_reader :good_dog, :has_been_walked
def initialize(good_or_not)
#good_dog = good_or_not
#has_been_walked = false
end
def walk_dog
#has_been_walked = true
end
end
describe Dog do
# helper method
def create_and_walk_dog(good_or_bad)
Dog.new(good_or_bad).walk_dog
end
it 'should be able to create and walk a good dog' do
dog = create_and_walk_dog(true)
expect(dog.good_dog).to be true
expect(dog.has_been_walked).to be true
end
end
Error Log:
C:\nital\my-data\my-sample-apps\Rails-Samples\rspec-samples\lib>rspec spec\helpers_demo_spec.rb
F
Failures:
1) Dog should be able to create and walk a good dog
Failure/Error: expect(dog.good_dog).to be true
NoMethodError:
undefined method `good_dog' for true:TrueClass
# ./spec/helpers_demo_spec.rb:26:in `block (2 levels) in <top (required)>'
Finished in 0.001 seconds (files took 0.33463 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./spec/helpers_demo_spec.rb:24 # Dog should be able to create and walk a good dog

The RSpec way of achieving what you want to do is to use blocks such as subject, let, before, after, etc.
describe Dog do
context 'good dog' do
subject { Dog.new(true) }
before(:each) do
subject.walk
end
it 'should be a good dog' do
expect(subject.good_dog).to be true
end
it 'should be a walked dog' do
expect(subject.has_been_walked).to be true
end
end
end

Your helper method returns either a TrueClass or a FalseClass while your spec expects a Dog instance. Your helper methods needs to return a Dog instance. You should update your code to look like this:
def create_and_walk_dog(good_or_bad)
dog = Dog.new(good_or_bad)
dog.walk_dog
dog
end

def create_and_walk_dog(good_or_bad)
Dog.new(good_or_bad).walk_dog
end
You don't want walk_dog to be called here. It returns true, which gives you your error.
def create_and_walk_dog(good_or_bad)
Dog.new(good_or_bad)
end

Related

Rspec allow and expect the same method with different arguments

I want to test my method which runs method Temp::Service.run two times inside it:
module Temp
class Service
def self.do_job
# first call step 1
run("step1", {"arg1"=> "v1", "arg2"=>"v2"})
# second call step 2
run("step2", {"arg3"=> "v3"})
end
def self.run(name, p)
# do smth
return true
end
end
end
I want to test arguments provided to second call of method :run with first argument 'step2'
while I want to ignore the first call of the same method :run but with first argument 'step1'.
I have the RSpec test
RSpec.describe "My spec", :type => :request do
describe 'method' do
it 'should call' do
# skip this
allow(Temp::Service).to receive(:run).with('step1', anything).and_return(true)
# check this
expect(Temp::Service).to receive(:run) do |name, p|
expect(name).to eq 'step2'
# check p
expect(p['arg3']).not_to be_nil
end
# do the job
Temp::Service.do_job
end
end
end
but I got error
expected: "step2"
got: "step1"
(compared using ==)
How to correctly use allow and expect for the same method ?
Seems like you are missing the .with('step2', anything)
it 'should call' do
allow(Temp::Service).to receive(:run).with('step1', anything).and_return(true)
# Append `.with('step2', anything)` here
expect(Temp::Service).to receive(:run).with('step2', anything) do |name, p|
expect(name).to eq 'step2' # you might not need this anymore as it is always gonna be 'step2'
expect(p['arg3']).not_to be_nil
end
Temp::Service.do_job
end

rspec testing subclass sti methods rails 4

I am trying to build an rspec test for a method on an sti subclass and the test only reads the parent model's method. The method works in the app, just not in the rspec test. I can't figure out what I'm missing
models/animals/animal.rb
class Animal < ActiveRecord::Base
def favorite
"unicorn"
end
end
models/animals/mammal_animal.rb
class MammalAnimal < Animal
def favorite
"whale"
end
end
models/animals/cat_mammal_animal.rb
class CatMammalAnimal < MammalAnimal
def favorite
"tabby"
end
end
mammal_animal_spec.rb
require 'rails_helper'
RSpec.describe MammalAnimal, type: :model do
let(:cat_mammal_animal) {FactoryGirl.create(:cat_factory)}
subject(:model) { cat_mammal_animal }
let(:described_class){"MammalAnimal"}
describe "a Cat" do
it "should initialize successfully as an instance of the described class" do
expect(subject).to be_a_kind_of described_class
end
it "should have attribute type" do
expect(subject).to have_attribute :type
end
it "has a valid factory" do
expect(cat_mammal_animal).to be_valid
end
describe ".favorite " do
it 'shows the favorite Cat' do
expect(cat_mammal_animal.type).to eq("CatMammalAnimal")
expect(cat_mammal_animal.favorite).to include("tabby")
expect(cat_mammal_animal.favorite).not_to include("whale")
expect(cat_mammal_animal.favorite).not_to include("unicorn")
print cat_mammal_animal.favorite
end
end
end
end
error
Failures:
1) MammalAnimal.favorite and .favorite shows the favorite Cat
Failure/Error: expect(cat_mammal_animal.type).to include("tabby")
expected "unicorn" to include "tabby"
# ./spec/models/mammal_animal_spec.rb:82:in `block (3 levels) in <top (required)>'
UPDATE
animals.rb
FactoryGirl.define do
factory :animal do
type 'Animal'
name "dragon"
trait :mammal do
type 'MammalAnimal'
name "zebra"
end
trait :cat do
type 'CatMammalAnimal'
name "calico"
end
factory :mammal_factory, traits: [:mammal]
factory :cat_factory, traits: [:cat]
end
end
as per a suggestion, I added the below line to the test
expect(cat_mammal_animal.class.constantize).to eq(CatMammalAnimal)
and got this error
1) MammalAnimal.favorite and .favorite shows the favorite Cat
Failure/Error: expect(cat_animal_mammal.class.constantize).to eq(CatMammalAnimal)
NoMethodError:
undefined method `constantize' for #<Class:0x007f8ed4b8b0e0>
Did you mean? constants
I think instead of using trait to create objects of subclasses, you should have separate factories for those too.
factory :animal do
name 'dragon'
end
factory :mammal, class: MammalAnimal do
name 'zebra'
end
factory :cat, class: CatMammalAnimal do
name 'calico'
end
All of these can be defined in animals.rb
Then you can create your objects like
create(:animal)
create(:mammal)
create(:cat)

How to test that a method is called using rspec?

In a model spec, I want to test that certain methods are being called correctly.
#models/object.rb
class Object < ActiveRecord::Base
after_validation :do_this
after_save :enqueue_that
def do_this
# does some stuff, the results of which I don't want to test
end
def enqueue_that
MyWorker.perform_later id
end
end
#spec/models/object.rb
describe Object
describe '#do_this' do
it 'is called on save with passing validations' do
object.save
expect(object).to receive(:do_this)
end
end
describe '#enqueue_that' do
it 'is called after save' do
object.save
expect(MyWorker).to receive(:perform_later).once
end
end
end
The tests are failing with the following
Failure/Error: expect(object).to receive(:do_this).once
(#<Object:0x007fd2101c7160>).do_this(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
Failure/Error: expect(MyWorker).to receive(:perform_later).once
(MyWorker (class)).perform_later(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
Confusingly, these methods appear to be behaving correctly in the dev environment.
Am I using expect().to receive correctly? Or have my tests uncovered a genuine bug?
You just have things in the wrong order...
it 'is called on save with passing validations' do
expect(object).to receive(:do_this)
object.save
end

Rails tutorial, chapter 6. error test: User return value of authenticate method with invalid password should be false [duplicate]

I am running this portion of a test:
describe Dictionary do
before do
#d = Dictionary.new
end
it 'can check whether a given keyword exists' do
#d.include?('fish').should be_false
end
With this code:
class Dictionary
def initialize
#hash = {}
end
def add(new_entry)
new_entry.class == String ? #hash[new_entry] = nil : new_entry.each { |noun, definition| #hash[noun] = definition}
end
def entries
#hash
end
def keywords
#hash.keys
end
def include?(word)
if #hash.has_key?(word)
true
else
false
end
end
end
I don't know what I'm doing wrong, but my tests keep failing and saying this:
> 1) Dictionary can check whether a given keyword exists
> Failure/Error: #d.include?('fish').should be_false
> expected false to respond to `false?`
I am confused at the error since it seems to be giving the correct answer. I would really appreciate if someone could take a few minutes to tell me what's wrong with my code.
Thank you tons.
If you browse the RSpec Expectations 2.99 and RSpec Expectations 2.14 and search the section - Truthiness and existentialism, you will find
expect(actual).to be_true # passes if actual is truthy (not nil or false)
expect(actual).to be_false # passes if actual is falsy (nil or false)
# ...............
# ...
But of you browse RSpec Expectations 3.0 , the above method names got changed to -
expect(actual).to be_truthy # passes if actual is truthy (not nil or false)
expect(actual).to be true # passes if actual == true
expect(actual).to be_falsey # passes if actual is falsy (nil or false)
# ...........
#......
It seems you are in 3.0, and using the method which were exist prior to this version. Thus you were getting the error.
I put the code in my test.rb file as below :-
class Dictionary
def initialize
#hash = {}
end
def add(new_entry)
new_entry.class == String ? #hash[new_entry] = nil : new_entry.each { |noun, definition| #hash[noun] = definition}
end
def entries
#hash
end
def keywords
#hash.keys
end
def include?(word)
if #hash.has_key?(word)
true
else
false
end
end
end
And my spec/test_spec.rb file is -
require_relative "../test.rb"
describe Dictionary do
before do
#d = Dictionary.new
end
it 'can check whether a given keyword exists' do
#d.include?('fish').should be_false
end
end
Now I am running the code from my console, and it works :
arup#linux-wzza:~/Ruby> rspec -v
2.14.8
arup#linux-wzza:~/Ruby> rspec spec
.
Finished in 0.00169 seconds
1 example, 0 failures
Now I am changing the code in my spec/test_spec.rb file :-
require_relative "../test.rb"
describe Dictionary do
before do
#d = Dictionary.new
end
it 'can check whether a given keyword exists' do
#d.include?('fish').should be_falsey
end
end
and again run the test :-
arup#linux-wzza:~/Ruby> rspec -v
2.14.8
arup#linux-wzza:~/Ruby> rspec spec
F
Failures:
1) Dictionary can check whether a given keyword exists
Failure/Error: #d.include?('fish').should be_falsey
NoMethodError:
undefined method `falsey?' for false:FalseClass
# ./spec/test_spec.rb:9:in `block (2 levels) in <top (required)>'
Finished in 0.00179 seconds
1 example, 1 failure
Failed examples:
rspec ./spec/test_spec.rb:8 # Dictionary can check whether a given keyword exists
arup#linux-wzza:~/Ruby>
Now, they also mentioned in the 3.0.0.beta1 / 2013-11-07 changelog
Rename be_true and be_false to be_truthy and be_falsey. (Sam Phippen)

Accessing session from a helper spec in rspec

I have a method in my ApplicationHelper that checks to see if there are any items in my basket
module ApplicationHelper
def has_basket_items?
basket = Basket.find(session[:basket_id])
basket ? !basket.basket_items.empty? : false
end
end
Here is my helper spec that I have to test this:
require 'spec_helper'
describe ApplicationHelper do
describe 'has_basket_items?' do
describe 'with no basket' do
it "should return false" do
helper.has_basket_items?.should be_false
end
end
end
end
however when I run the test i get
SystemStackError: stack level too deep
/home/user/.rvm/gems/ruby-1.9.3-p194/gems/actionpack-3.2.8/lib/action_dispatch/testing/test_process.rb:13:
From debugging this i see that session is being accessed in ActionDispatch::TestProcess from #request.session, and #request is nil. When i access the session from my request specs #request is an instance of ActionController::TestRequest.
My question is can I access the session object from a helper spec? If I can, how? And if I cant what is the best practice to test this method?
****UPDATE****
This was down to having include ActionDispatch::TestProcess in my factories. Removing this include sorts the problem.
can I access the session object from a helper spec?
Yes.
module ApplicationHelper
def has_basket_items?
raise session.inspect
basket = Basket.find(session[:basket_id])
basket ? !basket.basket_items.empty? : false
end
end
$ rspec spec/helpers/application_helper.rb
Failure/Error: helper.has_basket_items?.should be_false
RuntimeError:
{}
The session object is there and returns an empty hash.
Try reviewing the backtrace in more detail to find the error. stack level too deep usually indicates recursion gone awry.
You are testing has_basket_items? action in ApplicationHelper, which check a specfic basket with a basket_id in the baskets table, so you should have some basket objects in your test which you can create using Factory_Girl gem.
Hers's an example :-
basket1 = Factory(:basket, :name => 'basket_1')
basket2 = Factory(:basket, :name => 'basket_2')
You can get more details on How to use factory_girl from this screen cast http://railscasts.com/episodes/158-factories-not-fixtures
It will create a Factory object in your test database. So, basically you can create some factory objects and then set a basket_id in session to check for its existence like this :
session[:basket_id] = basket1.id
So, your test should be like this :-
require 'spec_helper'
describe ApplicationHelper do
describe 'has_basket_items?' do
describe 'with no basket' do
it "should return false" do
basket1 = Factory(:basket, :name => 'basket_1')
basket2 = Factory(:basket, :name => 'basket_2')
session[:basket_id] = 1234 # a random basket_id
helper.has_basket_items?.should be_false
end
end
end
end
Alternatively, you can check for a basket_id which is being created by factory_girl to be_true by using :
session[:basket_id] = basket1.id
helper.has_basket_items?.should be_true

Resources