On Wed, Aug 27, 2008 at 1:20 PM, Lake Denman <[EMAIL PROTECTED]> 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 sucks!") 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 > self.save > return true > end > end > > 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 > Tests) 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? No > 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 example :) > 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 Cheers, Pat _______________________________________________ rspec-users mailing list rspec-users@rubyforge.org http://rubyforge.org/mailman/listinfo/rspec-users