On 2010-03-20 7:02 AM, Patrick J. Collins wrote:
On Sat, 20 Mar 2010, Phillip Koebbe wrote:
Welcome to RSpec, Patrick. For some of us, it's pretty rocky at first. I
Thank you Phillip for your great explanation...
After reading what you wrote, I have a few questions:
1. From what I understand of what you wrote, stub_model makes a fake object,
with accessor methods for the AR columns, and they return nil.
So what happens behind the scenes with mock_model? What is the difference?
Looking at http://rspec.rubyforge.org/rspec-rails/1.3.2/, I can't see
any difference between mock_model and stub_model. David?
2. I sort of asked this earlier, but didn't really get addressed--- What is
the benefit to doing stubbing
vs. using a factory or fixture?
I passed on this question since I am not very familiar with projects
like factory_girl or machinist. I'd like to look into them more at some
point, but just don't have the time right now. As for fixtures, I can't
stand them. We used fixtures on that project I mentioned earlier. At
first, they were great. But as the project grew, it was a nightmare
keeping them working right. I *much* prefer mocking and stubbing.
3. In your code, you wrote:
context 'when record is not found' do
SomeClass.should_receive(:find).and_return(nil)
I just wondered about this, because-- correct me if I am wrong, but a record
not found would not return
nil-- it returns a record not found error, correct? Or does an error actually
equate to nil? I know in a
lot of my controllers to avoid record not found errors, I do .find_by_id
instead of .find, because that
will return nil if it's not found.
Right. I goofed there. It should have been .find_by_id. The point that I
was hoping to make, though, is that you can create the environment
needed to test what your controller does when certain conditions are
met. Consider the example of wanting to redirect to an error page if a
resource is not found:
context 'resource is found' do
before :each do
@some_object = stub_model(SomeClass)
SomeClass.should_receive(:find_by_id).and_return(@some_object)
get :show, :id => 1
end
it 'should render the show page' do
# not sure if my syntax is correct here...i use remarkable which
has a macro for it
response.should render(:show)
end
end
context 'resource is not found' do
before :each do
SomeClass.should_receive(:find_by_id).and_return(nil)
get :show, :id => 1
end
it 'should redirect to error page' do
# not sure if my syntax is correct here...i use remarkable which
has a macro for redirects
response.should redirect_to error_path
end
end
You are validating that the controller code works correctly. It would
look like:
def show
@some_object = SomeClass.find_by_id(params[:id])
redirect_to error_path and return unless @some_object
end
4. should_receive... So, if you have a method that is calling other class and
instance methods, isn't it
a given that those calls are important and that they should be expected? In
other words, I am finding
myself asking the same question again-- Why use stub instead of should_receive?
When would you actually
not care whether a method gets called or not if you are testing something. I
mean, I would assume if I am
testing something, I want to test everything that happens-- so any real code in
my app that deals with
class/instance methods, those seem like they would be important and therefore
those calls should be
expected. ?
Think about testing in isolation, but now isolating specific pieces of
functionality in your controller. If I have already tested one piece
with expectations, when I move on to another piece, I can just stub them
and focus on the code at hand.
This example isn't necessarily controller-specific, but maybe it will
help see the difference. Suppose you have a method that performs some
calculation. At one point in your testing, you should verify that the
method gets called when you expect it to. That's when you would use
.should_receive. At this point, you don't really care what the result
is, just that your application code is invoking the calculation at the
right time. Now, when you are validating what the application does *in
response* to the result of the calculation, that's when you would just
stub it since you don't really care about the calling of it, but what it
returns. So if you want to do one thing when the result is A and another
when it is B, you would stub it .and_return(A) in one case and
.and_return(B) in the other.
5. Since I learn really well from example, I just wrote up some random and
strange code, which does a
handful of things, and I would love it if you could go through and spec it--
and explain each step of the
way, what you're doing and why. What is necessary to test, and what is not,
and of course also-- why.
def index
type = params[:foo]
value = params[:bar]
@foo = type&& value ? Foo.send("find_by_#{type}", value) : Foo.all
@baz = /.*lower/.match(params[:baz]) ? @foo.lowered_name :
@foo.upper_name
flash[:notice] = "Something with #...@foo.numbers}" unless
params[:bool].to_i.zero?
respond_to do |format|
format.html { render 'foo_listing' }
format.js { render :json => { :foo => @foo }.to_json
format.pdf { render :inline => @foo.pdf }
end
end
#
class Foo< AR::base
def lowered_name
name.downcase
end
def upper_name
name.upcase
end
def numbers
rand(id)
end
def pdf
# return some prawn pdf object
end
...
If you could do that for me, it would be incredibly awesome...
I would be glad to help in this way, but it will have to be later today.
And instead of using completely made-up code, I'll use some of my
real-world code and tests as an example. I'll gist them later, unless
one of the gurus comes along and provides enlightenment for you!
Peace,
Phillip
_______________________________________________
rspec-users mailing list
rspec-users@rubyforge.org
http://rubyforge.org/mailman/listinfo/rspec-users