3

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?

3 Answers 3

3

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
Sign up to request clarification or add additional context in comments.

2 Comments

Why did that change anything? I refactored a test of mine exactly like you have it, but it still didn't work. Any ideas why and/or what RSpec is doing behind the scenes?
This has nothing to do with RSpec behind the scene, just simple logic. Before he changed it, the problem was .create was called before the .should test. After the change, .build is used to build the record, keep in mind at this point, the record is not created yet. Then he called .save to actually create the record. That's why it works.
1

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.

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.