Scott T. wrote:
over 6
avoided because the test suite was failing, but I had gotten
success recently but there are a few spots where just simple insert and
logic. If what I have said doesn’t make sense I can given an example of
what I mean.
Doesn’t this violate the rule of “never stub the method under test?”
Scott
I probably didn’t explain that very well. Simple example:
class WidgetRepository
def self.some_method_that_isssues_complex_sql_to_find_the_data(*args)
…
end
end
describe WidgetRepository,
“.some_method_that_isssues_complex_sql_to_find_the_data” do
#allow this one to hit the db
it “should blah, blah…” do
widget = create_widget
…(more setup)…
WidgetRepository.some_method_that_isssues_complex_sql_to_find_the_data(*args).should
== expected_results
end
…
end
Then we have an object that uses the data:
class WidgetYieldCalculator
def calculate(widget, other_stuff)
data =
WidgetRepository.some_method_that_isssues_complex_sql_to_find_the_data(some_args)
…operate on data…
return result
end
end
Now to spec it, since the repository SQL call is all speced out, we can
simply stub the call to WidgetRepository.some_method… and rely on the
fact that the Respository will do it’s job:
describe WidgetYieldCalculator, “#calculate” do
it “should calculate the correct yield” do
# given
data = […]
widget = …
WidgetRepository.stub!(:some_method_that_isssues_complex_sql_to_find_the_data).and_return(data)
#depending on the situation and spec an expectation is
better than a stub
# when
result = WidgetYieldCalculator.new.calculate(widget, options)
# then
result.should == expected_results
end
end
Sorry I don’t have more time to do a better example or explain it
better. Basically, I was just advocating a separation of
responsibilities: the data retrieval and operation on the data. This
then allows you to mock your interface of the repository at a higher
level. As I said, with the ActiveRecord pattern this kind of thinking
seems a bit odd. In my rails projects the “repository” calls are
usually class methods on an AR class. The big gain for this is that you
can then test all your business logic in the WidgetYieldCalculator by
varying the data which is stubbed. This reduces the number of DB calls
considerably. You will need an integration spec for both of these parts
to work together, and lately I have been starting with a story to drive
the entire process. I wouldn’t got to such lengths on simple cases, but
the current project I’m working on has driven me to do this in a number
of places where lots of data and objects are involved.
To answer your question more directly, I never stub a method of an
object that I am testing… I just stub the collaborator’s.
Hope that clears it up a little,
Ben