Testing after_create with rspec - ruby

I have code in my model.
class Foo < ActiveRecord::Base
after_create :create_node_for_foo
def create_node_for_user
FooBar.create(id: self.id)
end
end
and have code in rspec of Foo model
describe Foo do
let (:foo) {FactoryGirl.create(:foo)}
subject { foo }
it { should respond_to(:email) }
it { should respond_to(:fullname) }
it "have mass assignable attributes" do
foo.should allow_mass_assignment_of :email
foo.should allow_mass_assignment_of :fullname
end
it "create node in graph database" do
foo1 = FactoryGirl.create(:foo)
FooBar.should_receive(:create).with(id: foo1.id)
end
end
but my test is failing with message
Failures:
1) Foo create node in graph database on
Failure/Error: FooBar.should_receive(:create).with(id: foo1.id)
(<FooBar (class)>).create({:id=>18})
expected: 1 time
received: 0 times
What might be wrong?

Okay got around with problem
changed this
it "create node in graph database" do
foo1 = FactoryGirl.create(:foo)
FooBar.should_receive(:create).with(id: foo1.id)
end
to
it "create node in graph database" do
foo1 = FactoryGirl.build(:foo)
FooBar.should_receive(:create).with(id: foo1.id)
foo1.save
end

A bit late to the party but actually the above won't work. You can create a custom matcher to do it:
class EventualValueMatcher
def initialize(&block)
#block = block
end
def ==(value)
#block.call == value
end
end
def eventual_value(&block)
EventualValueMatcher.new(&block)
end
Then in your spec do:
it "create node in graph database" do
foo1 = FactoryGirl.build(:foo)
FooBar.should_receive(:create).with(eventual_value { { id: foo1.id } })
foo1.save
end
This means that the mock will not evaluate the block until after the fact and it has actually been set.

In case it helps someone, an updated version of Dan Draper solution to use a custom matcher with a block, would be like this:
# spec/support/eventual_value_matcher.rb
RSpec::Matchers.define :eventual_value do |expected|
match do |actual|
actual == expected.call
end
end
And usage:
require "support/eventual_value_matcher" # or you can do a global require on the rails_helper.rb file
it "xxx" do
foo1 = FactoryGirl.build(:foo)
expect(FooBar).to receive(:create).with(eventual_value(proc { foo1.id }))
foo.save!
end

Related

Searchkick not returning any results in RSpec

I'm trying to test a class with RSpec which makes use of Searchkick.
I have the following in my spec support directory:
RSpec.configure do |config|
config.before(:suite) do
# Reindex models
Restaurant.reindex
# and disable callbacks
Searchkick.disable_callbacks
end
config.around(:each, search: true) do |example|
Searchkick.callbacks(true) do
example.run
end
end
end
and my test looks like this:
describe Restaurants::SearchFacade, search: true do
subject { described_class }
describe '#search' do
context 'with a query' do
let!(:restaurant_1) { create(:restaurant, :reindex, description: 'Amazing sour dough pizza place') }
let!(:restaurant_2) { create(:restaurant, :reindex) }
it 'returns values containing the query' do
Restaurant.search_index.refresh
params = { search: { query: 'sour dough' } }
facade = subject.new(params)
expect(facade.search.total_count).to eq(1)
end
end
end
end
I should be receiving 1 result. However, Searchkick returns 0 results. This is not a problem with the facade since I've also tried with Restaurant.search('*') which should return all results but nothing is being returned.
My Restaurant class looks like this:
class Restaurant < ApplicationRecord
searchkick searchable: %i[title description street_address]
end
and my factory looks something like this:
FactoryBot.define do
factory :restaurant do
association :plan
association :company
sequence :title do |n|
"Restaurant #{n}"
end
description 'An awesome restaurant'
# More attributes here...
trait :reindex do
after(:create) do |restaurant, _evaluator|
restaurant.reindex(refresh: true)
end
end
end
end
Any idea what may be causing this? Any help would be much appreciated. Thanks in advance!
Try getting rid of all of that fancy stuff you are doing in your before(:suite) and your factories. You just need to be doing it before each spec (if the search tag is set).
I was able to get searchkick working with rspec and indexing my models with only this small amount of code in my spec_helper.rb:
RSpec.configure do |config|
config.before(:each) do |example|
# Elasticsearch / Searchkick
if example.metadata[:search]
Searchkick.enable_callbacks
Restaurant.reindex
else
Searchkick.disable_callbacks
end
end
end
Side note: You may still need to run the Restaurant.search_index.refresh code after creating a bunch of Restaurant records in your spec setup. I'm not entirely sure?

RSpec why is before(:each) never executed?

I have this simple code
require 'json'
module Html
class JsonHelper
attr_accessor :path
def initialize(path)
#path = path
end
def add(data)
old = JSON.parse(File.read(path))
merged = old.merge(data)
File.write(path, merged.to_json)
end
end
end
and this spec (reduced as much as I could while still working)
require 'html/helpers/json_helper'
describe Html::JsonHelper do
let(:path) { "/test/data.json" }
subject { described_class.new(path) }
describe "#add(data)" do
before(:each) do
allow(File).to receive(:write).with(path, anything) do |path, data|
#saved_string = data
#saved_json = JSON.parse(data)
end
subject.add(new_data)
end
let(:new_data) { { oldestIndex: 100 } }
let(:old_data) { {"test" => 'testing', "old" => 50} }
def stub_old_json
allow(File).to receive(:read).with(path).and_return(#data_before.to_json)
end
context "when given data is not present" do
before(:each) do
puts "HERE"
binding.pry
#data_before = old_data
stub_old_json
end
it "adds data" do
expect(#saved_json).to include("oldestIndex" => 100)
end
it "doesn't change old data" do
expect(#saved_json).to include(old_data)
end
end
end
end
HERE never gets printed and binding.pry doesn't stop execution and tests fail with message No such file or directory # rb_sysopen - /test/data.json
This all means that before(:each) never gets executed.
Why?
How to fix it?
It does not print desired message because it fails at the first before block. Rspec doc about execution order
It fails because you provided an absolute path, so it is checking /test/data.json
Either use relative path to the test ie. ../data.json (just guessing),
or full path.
In case of rails:
Rails.root.join('path_to_folder_with_data_json', 'data.json')

Writing a test for a case statement in Ruby

I'm trying to write a test for a case statement using minitest. Would I need to write separate tests for each "when"? I included my code below. Right now it just puts statements, but eventually it's going to redirect users to different methods. Thanks!
require 'pry'
require_relative 'messages'
class Game
attr_reader :user_answer
def initialize(user_answer = gets.chomp.downcase)
#user_answer = user_answer
end
def input
case user_answer
when "i"
puts "information"
when "q"
puts "quitter"
when "p"
puts "player play"
end
end
end
This answer will help you. Nonetheless I'll post one way of applying it to your situation. As suggested by #phortx when initializing a game, override the default user-input with the relevant string. Then by using assert_output we can do something like:
#test_game.rb
require './game.rb' #name and path of your game script
require 'minitest/autorun' #needed to run tests
class GameTest < MiniTest::Test
def setup
#game_i = Game.new("i") #overrides default user-input
#game_q = Game.new("q")
#game_p = Game.new("p")
end
def test_case_i
assert_output(/information\n/) {#game_i.input}
end
def test_case_q
assert_output(/quitter\n/) {#game_q.input}
end
def test_case_p
assert_output(/player play\n/) {#game_p.input}
end
end
Running the tests...
$ ruby test_game.rb
#Run options: --seed 55321
## Running:
#...
#Finished in 0.002367s, 1267.6099 runs/s, 2535.2197 assertions/s.
#3 runs, 6 assertions, 0 failures, 0 errors, 0 skips
You have to test each case branch. Via RSpec it would work that way:
describe Game do
subject { Game }
describe '#input' do
expect_any_instance_of(Game).to receive(:puts).with('information')
Game.new('i').input
expect_any_instance_of(Game).to receive(:puts).with('quitter')
Game.new('q').input
expect_any_instance_of(Game).to receive(:puts).with('player play')
Game.new('p').input
end
end
However due the fact that puts is ugly to test, you should refactor your code to something like that:
require 'pry'
require_relative 'messages'
class Game
attr_reader :user_answer
def initialize(user_answer = gets.chomp.downcase)
#user_answer = user_answer
end
def input
case user_answer
when "i"
"information"
when "q"
"quitter"
when "p"
"player play"
end
end
def print_input
puts input
end
end
Then you can test with RSpec via:
describe Game do
subject { Game }
describe '#print_input' do
expect_any_instance_of(Game).to receive(:puts).with('quitter')
Game.new('q').print_input
end
describe '#input' do
expect(Game.new('i').input).to eq('information')
expect(Game.new('q').input).to eq('quitter')
expect(Game.new('i').input).to eq('player play')
expect(Game.new('x').input).to eq(nil)
end
end

Test helpers with RSpec in Padrino (Sinatra)

I'm trying to test a helper in a Padrino (Sinatra) app. My helper method is itself calling Padrino core helper methods but they are undefined. The error appears only in RSpec, while the app works fine. So the way I'm including my helper in RSpec makes it loose "Padrino scope" but I don't know how to bring Padrino helper's scope properly in my RSpec environment.
My helper:
module AdminHelper
Sort = Struct.new(:column, :order)
def sort_link(model, column)
order = sorted_by_this?(column) ? 'desc' : 'asc'
link_to mat(model, column), url(:pages, :index, sort: column, order: order)
end
def sorted_by_this?(column)
column.to_s == #sort.column && #sort.order == 'asc'
end
end
Lenstroy::Admin.helpers AdminHelper
My spec:
describe AdminHelper do
before(:all) do
class AdminHelperClass
include AdminHelper
end
end
subject(:helper) { AdminHelperClass.new }
describe '#sort_link' do
context "with :pages and :title parameters" do
before do
sort = AdminHelperClass::Sort.new('title', 'asc')
helper.instance_variable_set('#sort', sort)
end
subject { helper.sort_link(:pages, :title) }
it { should match(/<a href=([^ ]+)pages/) }
end
end
end
Results in error:
1) AdminHelper#sort_link with :pages and :title parameters
Failure/Error: subject { helper.sort_link(:pages, :title) }
NoMethodError:
undefined method `mat' for #<AdminHelperClass:0x007f1d951dc4a0>
Including a helper where mat is defined doesn't work, as one method is dependent on another helper and it goes on and on...
Update
In my spec helper I have:
def app(app = nil, &blk)
#app ||= block_given? ? app.instance_eval(&blk) : app
#app ||= Lenstroy::Admin
#app.register Padrino::Helpers
#app.register Padrino::Rendering
#app
end
in my spec I have:
it "returns link to resource with sort parameters" do
app do
get '/' do
sort_link(:pages, :title)
end
end
get "/"
last_response.body.should =~ /<a href=([^ >]+)pages/
end
And now tests fail, last_response.body is ''.
Method #mat is defined in Padrino::Admin::Helpers::ViewHelpers. You can do
class AdminHelperClass
include Padrino::Admin::Helpers::ViewHelpers
include AdminHelper
end
Update:
If your methods are really dependent on all these routes and helpers you should consider doing full mockup of your app like this:
def mock_app(base=Padrino::Application, &block)
#app = Sinatra.new(base, &block)
#app.register Padrino::Helpers
#app.register Padrino::Rendering
# register other things
end
def app
Rack::Lint.new(#app)
end
mock_app do
get '/' do
sort_link(my_model, my_column)
end
end
get "/"
assert_equal "some test text", body
Here's how it's done in padrino-admin: https://github.com/padrino/padrino-framework/blob/master/padrino-admin/test/test_admin_application.rb
I was having the same problem (and getting very frustrated tracking down the modules and including them). So far, I've got my specs working by:
1) Explicitly defining my module (as explained in how to use padrino helper methods in rspec)
module MyHelper
...
end
MyApp::App.helpers MyHelper
2) Automatically including helpers at the top of my spec. (Right now I only have one helper spec, but in the future I might try to move this into spec_helper.rb.)
describe MyHelper do
let(:helpers) { Class.new }
before { MyApp::App.included_modules.each { |m| helpers.extend m } }
subject { helpers }
it 'blah' do
expect(subject.helper_method).to eq 'foo'
end
end

How would I rspec/test an updated_at field without using sleep() in ruby?

How do i write my spec without using the sleep(1.second) method? When I remove the sleep then my tests break because they are returning the same time stamp?
I have the following class method:
def skip
qs = find_or_create_by(user_id: user_id)
qs.set_updated_at
qs.n_skip += 1
qs.save!
end
and following spec:
qs = skip(user.id)
sleep(1.second)
qs2 = skip(user.id)
qs.should_not be_nil
qs2.should_not be_nil
(qs.updated_at < qs2.updated_at).should be_true
I've used the Timecop gem in the past for doing time based testing.
require 'timecop'
require 'test/unit'
class MyTestCase < Test::Unit::TestCase
def test_mortgage_due_in_30_days
john = User.find(1)
john.sign_mortgage!
assert !john.mortgage_payment_due?
Timecop.travel(Time.now + 30.days) do
assert john.mortgage_payment_due?
end
end
end
So your example may look like:
qs = skip(user.id)
Timecop.travel(Time.now + 1.minute) do
qs2 = skip(user.id)
end
qs.should_not be_nil
qs2.should_not be_nil
(qs.updated_at < qs2.updated_at).should be_true
This also works well for rspec tests. In your Gemfile:
require 'timecop', group: :test
Then, for example, you can use rspec to test a named scope that gets model called queries in descending updated_at order:
require 'timecop'
require 'spec_helper'
describe Query do
# test the named scopes for ordering and searching
describe 'when a query is searched or sorted' do
before :each do
#query1 = create(:query)
Timecop.travel(Time.now + 1.minute) do
#query2 = create(:query)
end
Timecop.travel(Time.now + 2.minute) do
#query3 = create(:query)
end
end
it 'should be listed in descending updated_at order' do
#queries = Query.order_by_latest
#queries.first.should == #query3
#queries.last.should == #query1
end
end
end

Resources