Pivotal Labs

Standup 08/08/2008

edit Posted by Sean Beckett on Friday August 08, 2008 at 04:27PM

Interesting Things

  • When using time zones in Rails 2.1, if you specify a zone, any datetime ActiveRecord attributes will be returned in that zone. E.g. if you specify Eastern Time, and then later request changed_at from an ActiveRecord, it will be returned in ET. However, if you ask for Time.now it is always returned in the local time zone, regardless of TZ settings. This isn't necessarily bad or unexpected behavior, but it can lead to test failures if you save a time to an ActiveRecord, get it back, and then compare the values. One workaround is to use Time.zone.now, which will always respect the current time zone, although this doesn't help with large existing codebases.

Ask for Help

"We're FlexMock for some of our Test::Unit unit tests, and recently added some new tests; nothing that is exercising new parts of the code or creating new mocks. However, for some reason when we call previously existing mocks we get errors from Rspec. These are not exceptions or assertion failures, but full-stop errors as if there were a syntax error. Turns out Rspec reopens the Test::Unit::TestCase class and overwrites some behavior, although the cause of the errors remain entirely unclear. Anyone know why it would do that and how to prevent it in the future?"

A few people mumbled about Rspec magic, but actual help was not immediately forthcoming.

Taking a break from Rspec

edit Posted by Nathan Sobo on Friday August 17, 2007 at 06:37AM

On my next new project, I think I'm going back to Test::Unit. I've lost patience with Rspec, and it seems like I'm not alone. But I've spent more time praising and lobbying for it than some of its other current detractors, so I feel an explanation of my reversal is in order. First, I'll start off with what I like about Rspec, what got me to spend the time and energy switching to it to begin with.

I've always stressed that Rspec brings nothing fundamentally new to the table. For **'s sake, it's a testing framework. Setup. Teardown. Mocking. There's not a hell of a lot more to see. And that's okay. What's great about Rspec is that it lets you use real strings to label your test fixtures and cases. When I learned about it, I was struggling to name my test cases like sentences.

test_that_foo_does_bar_when_it_has_been_bazzed

Luckily, I'm a Dvorak typist so the underbar is close at hand, but not being able to freely compose descriptions of what I'm testing in a natural way can be very limiting. Writing helps me gather my thoughts, so the act of labelling the test can really help me. I take special pride in well written "it" strings. The describe block strings are also helpful, but not as big a deal to me. The clever assertion hacks are also cute and fun to write. The built in mocking is nice, but I'm not a big mockofascist, so I don't get too excited about it.

And... that's basically it. My appreciation for Rspec can be broken down as follows:

  • 70%: String test fixture and case names
  • 20%: .should be_valid etc
  • 10%: Mocking

Now, what sucks about Rspec snuck up on me. It boils down to 2 things:

  1. The framework is too f-ing complicated in its implementation.
  2. The framework is too presumptive about how I wish to organize my tests.

The second is an aesthetic gripe, which I insist is fair game, since the framework's merits are mainly aesthetic anyways. The first is a much deeper issue. The semantics of a Test::Unit test fixture are straightforward. The test fixture is a class. It contains methods beginning with the word test. A seperate instance of the class is created, in which each test runs. A setup and teardown method run before and after each method invocation.

And that is more or less all I need to know. There's some stuff I'd like to have, like a global setup / teardown akin to before(:all), but I can what's there without really understanding anything about the framework's implementation. It rides on the semantics of Ruby, and I understand Ruby because I use it every day.

Rspec, on the other hand, sends me down a labyrinthine path full of Ruby meta-object-protocol tricks to accomplish even the simplest of tasks. And trust me, I have a pretty solid grasp of the MOP. I love it, use it, and cringe when people refer to it as "magic" (it's like assembly programmers calling a for loop magic or something). But there's use and then there's overuse. I myself can be accused of both. The eval family of methods is great. Therein lies the power to implement DSL's with their own semantics that can diverge quite dramatically from Ruby. Therein also lies the problem. I like classes. I like inheritance. I like the object oriented model. So if software written in an object oriented language can get away with employing the basic object oriented tools to accomplish its mission, well then it by all means should. I don't have time to dig around the BehaviorEvalModule, or whatever else I've looked at in myriad diversions to get Rspec to do something of medium hardness.

So I guess that's it. Rspec does the basics really well. Ridiculously beautiful stuff. But venture beyond the limited tracks they've laid and you're in the jungle. So anyway. I'm not ready to write the whole thing off yet, but I am going to revisit Test::Unit for a while, see if I might not give myself what I miss from Rspec atop its simpler implementation. We'll see if I come back.

doing it again and again

edit Posted by Alex Chaffee on Wednesday August 08, 2007 at 04:23PM

Here's an RSpec trick I discovered yesterday. Sometimes when you're writing a test you want to loop over some precondition data. But if you do a loop inside your test (or spec), then all the cases will be subsumed in a single test method (or "it" block). This means you'll have the following problems:

  • The first case to fail will cause the rest of the cases not to run. It'd be nice to see them all in a single test run.
  • You won't take advantage of RSpec's cool self-documenting trick of labeling each it block with a full description of the failure, and it'll be harder to debug which case failed.
  • If you're calling into Rails (e.g. in a View spec), you'll only be able to call certain methods -- especially render -- once per test method. That means that you simply can't use a loop inside a method to collapse redundant tests into a single block.

Ruby to the rescue! Instead of looping inside your it block, loop outside your it block.