[rspec-users] Four Question From an RSpec Baby - Give me something to chew
pergesu at gmail.com
Wed Aug 27 14:46:09 EDT 2008
On Wed, Aug 27, 2008 at 1:20 PM, Lake Denman <lists at ruby-forum.com> wrote:
> The project has been written - around 10,000 lines of code, but
> certainly less than that to add tests to. There has been no official
> testing put into place, and I'd very much like to implement RSpec into
> the project for a few reasons, but mostly to setup solid examples for
> the behavior of the application.
> It was suggested to me to begin with Unit Tests. The User Model is
> almost 1000 lines with plenty of methods and no coverage, so I figured I
> would start there. After installing Rcov to track my progress, I wrote
> about 10 passing examples that were, I guess, pretty trivial - the
> methods I tested all dealt directly with an "instantiated user
> object"(is that the correct terminology?).
When trying to get a large, existing code base under test, I think
it's more valuable to begin with some very high-level tests that cover
a lot of ground. Basically, you're going for "something broke" rather
than "this specific thing broke" with these. As time goes on and you
develop high functional coverage over the code, you can write more
focused tests that will alert you to specific breakages. Also, keep
in mind that one of the primary benefits of unit testing is as a
design tool. When you're retrofitting tests, you don't get that
benefit (except for the frequent cases where you go "gosh, this design
However, taking a couple weeks off to write exhaustive tests for your
code base is basically never feasible. So you have to make tradeoffs.
The simplest approach is to write tests to cover any code that you're
changing. You'll need to spend some extra time and mental energy
analyzing the various pathways, because the code you're changing has
dependencies, and there are other dependencies on the code you're
change, all of which you have to discover and account for. "Working
Effectively with Legacy Code" by Michael Feathers has great
information on this.
So start off with high-level tests to maximize your value. At this
point, that means being alerted to regressions. The lowest level you
should probably go is controller tests...set up some state, hit the
action, verify that the leftover state is what you expect. Writing
even higher level tests with Cucumber would be an even better idea, I
think. It covers more of the stack and lets you actually take
pathways through the application. Actually now that I think about it,
a combo of acceptance tests and controller specs would probably be
best. Acceptance tests for happy paths, controller specs for testing
other paths at a slightly lower level.
> I have been writing the user examples in the order that a real user
> might usually take (A User who: is activating his account, is logging
> in, forgets his password and resets it, wants to use a new email
> address, wants to change his username.)
> Now, I'm not sure of the next step in the process, I'm stuck! The method
> (upgrade) takes a Payment object parameter. Inside the upgrade method,
> some payment object attributes are set (user_id, payment_type) and then
> saved. Then, the user object that is being upgraded has some attributes
> that are set (payment_id, member, member_since) and finally the user is
> saved, ending the method. And to top it all off, this method takes place
> in a transaction.
> For a visual guide:
> def upgrade(payment)
> transaction do
> payment.user_id = self.id
> payment.payment_type = Payment::SUBSCRIPTION_PAYMENT_TYPE
> return false unless (payment.save and payment.external_id)
> self.subscription_id = payment.external_id
> self.payment_id = payment.id
> self.member = true
> self.member_since = AppLib.today_utc
> return true
> Now that you have sufficient back story (I hope), here are my questions:
> 1.) Do I need to use any mocking/stubbing in this example (or in Unit
Sure, you can. As I suggested above, you'll want to get some
high-level functional coverage over the code you're changing. But
then you can zoom in and write some unit tests for it...and using mock
objects here will probably point out bad dependencies.
> 2.) Is it wrong to access multiple objects (user and payment in my
> example) in a Unit Test example?
> 3.) Would you mind showing me an example of how you might implement a
> spec for this method.
The ideal way is for the object to behave differently once changes
have been made. But, in Rails, model objects often just shuffle data
around and don't do anything particularly interesting. So the
interesting visible behavior is actually in the UI, or at a lower
level, the attributes on the models themselves. So for this spec
you'd probably just want to run it and make sure that the attributes
are what you expect them to be. Pretty easy, so I won't write an
> 4.) Could you PLEASE PLEASE PLEASE guide me to a resource that helped
> you the most with figuring out Unit Testing with RSpec. Not limited to
> books or blog posts... good source code examples might be helpful.
http://blog.davidchelimsky.net/ is a good starting point. Read his
articles, and then the blogs listed on the right side of the page.
Check out the Webrat and Merb projects, all their code is RSpec'd
More information about the rspec-users