Wednesday, June 22, 2011

Positive Ruby, avoiding Debbie Downers

Few developers are fortunate enough to work in an environment that they truly enjoy. And that's why it's even more important to keep your code positive! Nobody wants to read line after line of code negation through the use of 'not' or the almost invisible '!'. I like to refer to code that relies on the use of bangs and 'not's as Debbie Downers. Nobody likes a Debbie Downer, so stop torturing yourself and your team and leverage the positive aspects of Ruby.

For starters, take a look at this code:
def consume(food)
  if ! food.poisonous?
    eat(food)
  end
end

What's wrong with it? The bang is barely noticeable! This is because it is essentially a thin line and most humans are trained to look for bangs at the end of a sentence, not in the middle. And not noticing the bang in this situation can prove deadly. I also find myself asking, "well, if it's not poisonous, than what is it?" So clearly this code does not "Say what I mean".

A better way to approach this method would be to remove the bang and add a more meaningful method to our food class. For example:
def consume(food)
  if food.edible?
    eat food
  end
end

Not only is this approach more positive (thinking about edible food is always more enjoyable than thinking about poisonous food), but it clearly states our intent. This is white listing. Simply put, white listing is stating exactly what is allowed and denying everything else. In our case, we ONLY want to eat what is edible and nothing else. (more about white listing)


Moving on, how do you feel about this?
def consume(food)
  while ! food.gone?
    eat food
  end
end

Let us assume that we cannot add another method on food that would allow us to add an inverse method to 'gone?'. Fortunately Ruby provides us with the "until" control structure. Let's try it out:

def consume(food)
  until food.gone?
    eat food
  end
end

Once again, we've achieved a more readable and positive solution. I feel better already!

Now Debbie Downer has a friend, Miss Unless. Miss Unless is a temptress, so beware. At times you may find yourself wanting to use her in guard clauses and the like. To combat Miss Unless's seductions, use an 'if'. Stay positive and turn this:
def consume(food)
  eat food unless food.poisonous?
end

into this:
def consume(food)
  eat food if food.edible?
end

Ruby provides you with an extensive list of control structures, make sure you pick the ones that allow you to say what needs to be said clearly and positively.

Friday, June 17, 2011

Faking authentication in Rails with Devise and RSpec 2

There seems to be alot of different approaches out there on how to fake authentication in your controller specs when using Devise with Rails 3 and RSpec 2. In fact, Devise has even supplied it's own Devise::TestHelpers module for just this scenario. I however was unhappy with that approach and decided to dig a little deeper.

I found that by mocking Warden itself I was able to achieve leaner tests that did not require actually creating an instance of the user i was faking, nor did it require calling several helper methods.

Below is the snippet(gist) we now use in our application to fake authentication in our controller specs. I hope you find it useful.
module AuthenticationHelpers
  # Example usage: login mock_user(Editor)
  def login(user)
    request.env['warden'] = mock(Warden,
                                 :authenticate => user,
                                 :authenticate! => user)
  end
  
  def mock_user(type, stubs={})
    mock_model(type, stubs).as_null_object
  end
end

Saturday, May 21, 2011

Say what you mean!

5 years ago I wrote my first line of Ruby. It was at a coding dojo while pairing with Corey Haines. At the time I was deep into the world of .NET and C# and had not yet seen a line of Ruby code. So when it came time for me to take the keyboard and make something happen I didn't have a clue where to start. That's when Corey said, "Just write it how you think it should be written". So I did. And what I had written while not exact Ruby syntax, was pretty close!

"But Corey, what about testing in Ruby?", I said. Corey then introduced me to the most important Gem in all of Ruby (insert drumroll here) ... RSpec. I was already an avid TDDer in C# using NUnit, so testing code was very familiar to me. However, NUnit always seemed lacking. No matter how expressive I tried to make the tests they never seemed to convey thoughts as clearly as I'd like. RSpec was not like that at all! Within minutes of pairing with Corey writing specifications in RSpec, I realized I had found my soul mate in RSpec! It was awesome!! I was able to Say What I Meant in code without any ceremony.

Yukihiro Matsumoto said "Code is an expression of the thoughts, attitudes, and ideas of the programmer." When it comes to expression of thought, Ruby is definitely an enabler!

Regardless of what language you are using to build your next application for world domination, pay attention to how you convey your thoughts in your code. Because without attention to how you organize your code, it can be very easy to get lost in the code and take a wrong turn. And the next thing you know you are pointing your Mega Laser at the wrong place during runtime!! (Hopefully your tests covered pointing the laser and saved your arse)

Avdi Grimm takes expression to the next level, emphasizing that developers should confidently express themselves in their code. The first step in building confidence and clarity in your code is to establish a consistent narrative structure. Avdi's proposed narrative structure is this:

  1. Gather Input
  2. Perform Work
  3. Deliver Results
  4. Handle Failure

Now that you are armed with this narrative approach to writing your code, don't forget to just simply Say What you Mean! Your co-workers will thank you in the end.

Thursday, April 21, 2011

How much are you willing to pay to speed up your Rails tests?

The trending topic on twitter lately is about how fast can you make your tests run. Unfortunately what never gets mentioned in those twitter conversations is balance; the balance between speed and good specification of behavior.

Attila Domokos has been working on speeding up his tests and documented his efforts in his blog. While the effort is good, I have concerns about this type of approach and it's associated cost.

Unfortunately this approach relies heavily on mocking and stubbing, and not just to fake out one method, but several methods and objects. Mocking methods ties your tests tightly to the internal implementation of the object you are mocking at which point you are no longer testing behavior, but you are testing implementation. This is a serious concern and leads to brittle tests. This should be avoided.

My other concern is that this approach relies heavily on good integration level testing. Integration testing is cumbersome and I've yet to see a suite with more than a handful of tests that run in under a minute. Often times these test suites take 30-60 minutes to run for an application of even moderate size. So if our goal to keep our test suite running in under 10 seconds, all we've done is pushed our longer running tests into an integration suite that has a much larger feedback loop in exchange for brittle tests that save us a few hundredths of a second. It's like using a credit card. The immediate purchase was satisfying, however the debt collection was costly.

Now there are tools like spork and auotest that can help get shorter feedback loops and i suggest your team explore those options first before sacrificing the quality of your tests.

What I ask of the community is that when you're working with your test suite and trying to speed it up, please acknowledge any concessions you may be making in the name of speed and make a smart decision about how you proceed.

Tuesday, February 22, 2011

Testing drama when using Mongoid, S3, Paperclip, and RSpec

The team is in the early stages of writing a new CMS in Rails 3 to support our online publications. We've encountered a few interesting hurdles in our first Iteration, one of those coming in the form of a Mongoid/S3/Paperclip cocktail and how to test it.

Paperclip & Mongo

Thotbott has done it's due diligence and released Paperclip with packaged shoulda matchers. However ... since we are using Mongo through Mongoid we needed to introduce Mongoid-Paperclip into our project. Unfortunately this does cause problems with the shoulda matchers provided by Paperclip. In particular the should have_attached_file and should validate_attachment_content_type fail to work out of the box.

Should have_attached_file

We were already using mongoid-spec to test our Mongo documents and we decided to use that to our advantage. After inspecting the paperclip matcher implementation we discovered they essentially were checking for the existence of paperclip defined attributes on the class using the respond_to? method. This doesn't work so well with Mongo, so we followed the matchers pattern and created our own custom matcher that leverages mongo-spec's matchers. Here is the result:

RSpec::Matchers.define :have_mongoid_attached_file do |expected|
  match do |actual|
    have_fields(":#{actual}_content_type", ":#{actual}_file_name", ":#{actual}_file_size", ":#{actual}_updated_at")
  end
end

should validate_attachment_size

Interestingly enough, this worked fine. Win!

should validate_attachment_content_type

This however was not a Win, but a big Fail. validates_attachment_content_type works, but the matcher does not. We time boxed getting the matcher to work and failed to complete it in the time allowed. This remains an open item at the moment.

S3

Configuring our paperclipped class to use S3 was easy!! We added our s3.yml, told the class how to find it and to use S3. It wasn't long before we noticed the specs were taking 1.5 seconds longer to run; the 67 previously written specs ran in under 0.3 seconds. The only change we made was configuring S3.

After some serious inspection and several four letter words we discovered even our have_mongoid_attached_file was causing the app to establish a connection to S3!! We tried bringing in FakeWeb, mocking, stubbing, and more mocking and stubbing before the lightning struck ... remove S3 configuration from our test environment. Our paperclip declaration now looks like this:

  has_mongoid_attached_file :data, {
    :styles => {
      :thumb => "75x80>",
      :medium => "120x120>" },
    :path => "photos/:date/:id/:style/:filename"
  }.merge(::PAPERCLIP_CONFIG)

In our application.rb we've defined PAPERCLIP_CONFIG as:
    PAPERCLIP_CONFIG = {
      :storage => :s3,
      :s3_credentials => "#{Rails.root}/config/s3.yml"
    }
and we override that in our test.rb with a simple:
    PAPERCLIP_CONFIG = {}

Now our specs are running sub second again and there was peace without complicated mocking.

Note to self => We're looking to integrate the shoulda matchers for mongoid into Paperclip itself in a way that is agnostic of the database being used. IMO, it sucks that Paperclip is coupled to ActiveRecord and we've had to introduce monkey patches in the form of gems to override that coupling.