psyho / bogus

Fake library for Ruby
Other
359 stars 14 forks source link

Faking active_record classes #16

Closed elvanja closed 11 years ago

elvanja commented 11 years ago

I have issue with faking active record "where" method. An example:

class User < ActiveRecord::Base
end

class Task < ActiveRecord::Base
end

class ListTasks
  def initialize(user , task_repository = Task)
    @user = user
    @task_repository = task_repository
  end

  def all
    @task_repository.where(user_id: @user.id).order_by("priority asc")
  end
end

describe ListTasks do
  fake(:user, id: 100)
  fake(:task_repository) { Task }
  let(:subject) { described_class.new(user, task_repository) }

  it "fetches tasks for a given user" do
    subject.all
    expect(task_repository).to have_received.where(user_id: 100)
  end
end

Executing rspec gives:

Task:0x1d65a50 does not respond to where

I am using bogus v0.0.4.

wrozka commented 11 years ago

By default bogus fakes instances of classes, but here you need to fake the class, So you should create fake using:

fake(:task_repository, as: :class) { Task }

Here are the docs for faking classes.

elvanja commented 11 years ago

Great, that solved the issue, thank you for your time and a quick answer!

A side note: expect(task_repository).to have_received.where(user_id: 100) got me a bit confused since it is rather close to Test Spies in RSpec-Mocks. Just an observation.

elvanja commented 11 years ago

Found another problem, if you don't mind helping :-)

In another test I am trying to stub Task's user_id with a value, like this: stub(task).use_id { 666 }, and I get the following error:

Failure/Error: stub(task).user_id { stub.id { 101 } }
NameError: <Task:0x1ae24e0> does not respond to user_id

I've read the Bogus Safe Stubbing chapter, and here's what I wrote:

This time, the tasks belong to a user:

class User < ActiveRecord::Base
  has_many :tasks
end

class Task < ActiveRecord::Base
  belongs_to :user
end

class UpdateTask
  def initialize(user, task, task_repository = Task)
    ...
  end
end

describe UpdateTask do
  fake(:user)
  fake(:task)

  it "requires user and task user to match" do
      stub(user).id { 100 }
      stub(task).user_id { 101 }
      expect { described_class.new(user, task) }.to raise_error(ArgumentError)
  end
end

At the same time, stubbing the User property works just fine. The tests don't touch the database at all. Any idea why this is not working?

wrozka commented 11 years ago

Bogus doesn't handle methods defined on method_missing, as it would require calling respond_to? on the real instance. It copies only methods known at the fake creation. There is a workaround, that adds accessor methods for AR attributes based on schema,

psyho commented 11 years ago

Since the issue of stubbing fields on AR models seems tu come up pretty frequently, I added the workaround to the Bogus codebase.

elvanja commented 11 years ago

Yep, my bad, forgot about the way active record defines model methods. Was hoping I'd be able to avoid hitting the database :-( Anyway, thanks for the assistance!

wrozka commented 11 years ago

We've changed the mechanism for faking AR models a little. To avoid db interactions, you might consider using nulldb.