Unit testing by simplifying the problem: memoization

I see unit testing as a way to test each possible snippet of functionality and route the code in question can take. With Ruby being such a dynamic language and allowing shortcuts to common problems, sometimes it can seem somewhat of a mystery, how to test these snippets of functionality.

Using Memoization as an example:


class MyClass
  def lazy_initialized_value
    @lazy_initialized_value ||= Expensive.request
  end
end

There are actually 3 separate snippets of functionality that need testing here, however it is not immediately obvious from the example. Lets be slightly more verbose about what is actually happening:


class MyClass
  def lazy_initialized_value
    @lazy_initialized_value = Expensive.request unless @lazy_initialized_value
    @lazy_initialized_value
  end
end

Now it is much easier to see the 3 steps the code should take:

* Store result of expensive request in instance variable
* Leave instance variable alone when it is already set
* Return the value of the instance variable

Now we have this information, our tests become (using Mocha to mock external methods):


class Expensive; end

module Tests::MyClass
  # lazy_initialized_value
  # ----------------------
  class LazyInitializedValueTest < Test::Unit::TestCase
    def test_should_respond
      assert_respond_to MyClass.new, :lazy_initialized_value
    end
  
    def test_should_store_result_of_expensive_request_in_instance_variable
      instance = MyClass.new
      Expensive.stubs(:request).with().returns('expensive value')
      instance.lazy_initialized_value
      assert_equal 'expensive value', instance.instance_variable_get('@lazy_initialized_value')
    end
    
    def test_should_return_value_of_instance_varable
      instance = MyClass.new
      instance.instance_variable_set '@lazy_initialized_value', 'the value'
      Expensive.stubs(:request)
      assert_equal 'the value', instance.lazy_initialized_value
    end
    
    def test_should_maintain_existing_instance_variable_value_when_already_set
      instance = MyClass.new
      instance.instance_variable_set '@lazy_initialized_value', 'existing value'
      Expensive.stubs(:request)
      instance.lazy_initialized_value
      assert_equal 'existing value', instance.instance_variable_get('@lazy_initialized_value')
    end
  end
end

Now we have these tests in place, we can go back and refractor the code ’til our heart’s content using all the tricks in the book but by simplifying the problem in the first place, it gives us a solid test suite and the confidence to make changes without breaking functionality.

If you were solving this problem test-first then you wouldn’t (but more likely, shouldn’t) have written the first example until re-factoring stage anyway, however when these shortcuts become engrained in your brain, it’s all too easy to forget what they are _actually_ doing.

So there we go, simplify the initial implementation, get a solid test suite in order, _then_ re-factor.

Stubbing case statements with Mocha

I was happily mocking away with Mocha, then I passed a stub to a case statement at which point I was a little flummoxed.

The Ruby documentation clearly states that the ‘when’ in a ‘case’ statement uses the ‘===’ method for comparing the subject so I couldn’t work out why something like the following wasn’t working:


# Code
class A; end

def foo(instance)
  case instance
    when A : 'an A instance'
    else 'not an A instance'
  end
end

# Test
a = stub_everything
a.stubs(:===).with(A).returns(true)
assert_equal 'an A instance', foo(a)

So I had a little play around in irb and found the following:


class A; end
a = A.new
a === A #=> false
A === a #=> true

This revealed that I was actually stubbing the wrong side of the operator, I changed this to the following and voila!


# Code
class A; end

def foo(instance)
  case instance
    when A : 'an A instance'
    else 'not an A instance'
  end
end

# Test
a = stub_everything
A.stubs(:===).with(a).returns(true)
assert_equal 'an A instance', foo(a)

Looks obvious now but certainly wasn’t at the time!

Rails: Using Autotest with UnitRecord

Myself and a colleague have just managed to waste away a good couple of hours trying to figure out Autotests strange ‘style’ mechanism to add the ability to “test in the way Jay Fields explains”:http://blog.jayfields.com/2007/09/rails-how-we-test.html using “UnitRecord”:http://unit-test-ar.rubyforge.org/.

You can grab our plugin to enable “UnitRecord”:http://unit-test-ar.rubyforge.org/ when using “Autotest”:http://rubyforge.org/projects/zentest below:

“http://svn.soniciq.com/public/rails/plugins/iq_autotest”:http://svn.soniciq.com/public/rails/plugins/iq_autotest

By default, running autotest in the Rails directory will run the unit tests. To run the functional tests, do: AUTOTEST='functional' autotest

I hope this saves some people some time!!

SonicIQ Hiring! – UK, Ruby on Rails Developer Required

We are looking for a Ruby on Rails, XHTML & CSS Developer to join our team at “SonicIQ”:http://soniciq.com. Head over to “43folders job board to view our ad”:http://jobs.43folders.com/job/6a5255713e8351a5eb2efef7805b7629/?d=1.

These are exiting times with projects like “Propel’r”:http://propelr.com in the pipeline, along with the ever-growing opportunities for new and interesting client projects.

If you are a highly motivated developer and can see yourself in a Ruby on Rails position in sunny (sometimes) Bournemouth, UK then “apply at 43folders”:http://jobs.43folders.com/job/6a5255713e8351a5eb2efef7805b7629/?d=1.

Ruby Ternary operator re-written with Boolean operators

Just noticed something interesting (well not that interesting). Ternary operators in Ruby can be re-written using Boolean operators e.g.

method = object.respond_to?(:foo) ? :foo : :bar

would become…

method = object.respond_to?(:foo) && :foo || :bar

Simply replace ? with && and : with ||

I don’t know if there is any performance gain here, anyone care to investigate?

A Ruby reject! that returns the rejected items

I often need to do a reject! and return the rejected items instead of the modified collection. This saves having to do a select beforehand.

An example of how accomplish this:

options = { :a => 1, :b => 2, :c => 3 }
rejects = Hash[*options.select { |k, v| k == :b && options.delete(k) }.flatten]

assert_equal { :a => 1, :c => 3 }, options
assert_equal { :b => 2 }, rejects

This could be written as a method of the Hash class and an alternative for Array.

For Hash, the code would look something like:

class Hash
  def extract!
    Hash[*self.select { |k, v| yield(k, v) && self.delete(k) }.flatten]
  end
end

options = { :a => 1, :b => 2, :c => 3 }
rejects = options.extract! { |k, v| k == :b }

assert_equal { :a => 1, :c => 3 }, options
assert_equal { :b => 2 }, rejects

If I am missing something obvious in Ruby that accomplishes the same, please leave a comment.

Building Ruby, Rails, Subversion, Mongrel, and MySQL on Mac OS X Tiger – The Easy Way

“Dan Benjamin”:http://hivelogic.com/authors/danbenjamin recently updated his very helpful article entitled “”Building Ruby, Rails, Subversion, Mongrel, and MySQL on Mac OS X”:http://hivelogic.com/narrative/articles/ruby-rails-mongrel-mysql-osx”.

I don’t know about anyone else but compiling software is not one of my favourite pastimes. As I’ve been chopping and changing macs lately I thought I’d write a couple of shell scripts to get things right before running anything on my nice new (freshly installed) MacBook Pro.

h3. Prerequisites

There are a couple of prerequisites in addition to what’s on Dan’s “”What’s Needed”:http://hivelogic.com/narrative/articles/ruby-rails-mongrel-mysql-osx” list before running “the scripts”:http://svn.soniciq.com/public/rails/tools/osx_development_setup/. The first is that you must have MySQL installed as referenced in “Dan’s article”:http://hivelogic.com/narrative/articles/ruby-rails-mongrel-mysql-osx, and the second being the following:

Ensure you have the following line at the end of your ~/.bash_login file:

export PATH="/usr/local/bin:/usr/local/sbin:/usr/local/mysql/bin:$PATH"

You can add this by typing nano ~/.bash_login then copy and paste the above line into the end of the file. Once this is done, hit ctrl-x to exit, answering ‘y’ to “do you want to save changes” prompt.

h3. The scripts

There are two scripts, “01_osx_rails.sh”:http://svn.soniciq.com/public/rails/tools/osx_development_setup/01_osx_rails.sh and “02_osx_image_tools.sh”:http://svn.soniciq.com/public/rails/tools/osx_development_setup/02_osx_image_tools.sh and the following will explain how to use them.

h3. Creating your development environment

The first script will install everything in Dan’s article along with the following gems that I use frequently:
* rails version 1.1.6 for support of older rails apps
* bluecloth
* redcloth
* sqlite3-ruby
* ferret
* ZenTest
* redgreen

Right… here we go:

(please read “the licence”:http://svn.soniciq.com/public/rails/tools/osx_development_setup/MIT-LICENCE before running these scripts as although they have been tested on a clean install of Tiger, I can’t take any responsibility if something breaks)
# Download “01_osx_rails.sh”:http://svn.soniciq.com/public/rails/tools/osx_development_setup/01_osx_rails.sh to your desktop
# If you don’t wish to install all of these gems then open the file in a text editor and comment out the relevant lines with a hash.
# Open a new terminal window and type the following:

cd ~/Desktop
sh 01_osx_rails.sh


# Go make a cup of tea and watch all the pretty text scroll before your eyes.

That’s it, you should now have a fully working development environment.

h3. Image tools

I use a couple of image tools on my system, GD and ImageMagick (with RMagick). If you wish to install these tools and the related libraries, run the following:

(This script hasn’t been tested as much as the previous one so use at your own risk. If anyone has any bug fixes then please post a comment)

cd ~/Desktop
sh 02_osx_image_tools.sh

h3. We’re done

Please let me know if I’ve missed anything obvious.

I hope this helps anyone else out there with Compilaphobia!

Ruby on Rails Exchange, London

Last Friday, a couple of us from “SonicIQ”:http://www.soniciq.com went to the first “RoR eXchange hosted by Skills Matter”:http://www.skillsmatter.com/rorexchange.

It was a great day with some interesting talks from:

* Chad Fowler
* Tom Locke
* Paul Battley
* James Cox
* Damien Tanner
* Ben Griffiths
* Eleanor McHugh

h3. Chad Fowler – Quick and -Dirty- Clean: Well factored Rails

Chad kicked off with a talk on “Quick and -Dirty- Clean: Well factored Rails”. For me it was one of those talks where you think “If only I heard this 6 months ago!”. The reason I say this is that a great deal of what he was talking about, we now implement on a day-to-day basis, but we have gotten there the hard way through making the mistakes that he mentions.

He talks about never using instance variables in views, I love that someone has confirmed this as I find it generally bad practice. For a while now we have been replacing them with helpers giving much more flexibility when the controller logic changes.

One great point that was made, was that SQL should never be used in controllers, not even in :order options. I have to admit, in the past I have done this a lot but the suggestion of custom finders in the model are a much better alternative and it keeps with the whole “Skinny Controller, Fat Model”:http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model idea.

One thing I really must get into the habit of using are association proxies for has_many and such like. Chad showed some great examples of this.

I like one phrase that Chad came up with… “Bare Model Finds” which made me chuckle. What he was getting at was something like an account based site where everything should be found via the given account i.e. @account.projects.find(:all)

as opposed to a “Bare Model Find” of Project.find(:all, :conditions => [‘account_id = ?’, @account.id])

The main points I have taken away from this talk (which I am also going to put in a large poster on the office wall) are the folowing:

* No instance variables in views
* No SQL in controllers
* No more than 4 lines in controller actions

h3. Tom Locke – Experiments with Very Rapid Development in Rails

Tom gave a demo of his “Hobo”:http://hobocentral.net/blog/ project which is very similar to a plugin we use in-house (which also may go open source at some point).

One thing I like about Hobo is the incremental nature of the “dryml” views, meaning that you can override certain elements in a default view bit-by-bit, i.e. if you just want a different header for a certain page you don’t need to create a duplicate of the view and change it, you only need to redefine certain elements of the page.

I completely agree with Tom’s views on generators, although he was slightly more diplomatic than I would have been. I am going to be slightly controversial and say that I really don’t like generators except for creating stub files. What happens if you have an authentication system generator which you have used in 50 projects and you suddenly find a security hole?? …You now have to go back through those 50 projects to fix it! [end generator rant]

h3. Paul Battley – Rerouting Rails

Paul attempted to explain how he believes the rails routing system should be re-written. I could see a lot of merit in what he was saying, however I don’t believe it lent itself very well to this type of presentation.

h3. James Cox – Managing a high performance rails app without tearing your hair out

James gave some great tips on easy ways to increase the overall performance of Rails apps. We are in the process of configuring a new server so these tips came at a good time.

h3. Damien Tanner – Extend your Rails application using Domain Specific Languages

Damien explained a roundtrip his company went on with DLS’s explaining that they are great in certain situations but can be taken too far or overused. In the example he gave, it turned out that the DSL he created was overkill for the business problem he was trying to solve.

h3. Ben Griffiths – Are your tests working for you?

For me, this and Chad’s were the presentations that made the trip to London worth while. I am ashamed to say that until now, I have not gotten into TDD (I’ve been using Rails for over a year and a half). It’s not through lack of trying but with clients constantly on your back to get projects out the door, it’s all to easy to dive straight into coding the application and neglecting the tests.

He started with a comparison between testing and the film Predator. This sounds like a strange comparison however it really helped to illustrate the need for a good set of tests.

My main problem with tests were “what do I test?” and “my fixtures need too much maintenance!”. I breathed a sigh of relief when Ben suggested not using fixtures at all as maintaining a load of fixtures really turned me off testing.

This was a great talk and I could bang on for hours on the benefits of testing with Ben’s techniques and his whole take on modular design but I’ll save that for another post. As a side-note, I have been doing TDD ever since this talk.

h3. Eleanor McHugh – Where’s my SQL? Platform-independent database design using Migrations

Eleanor gave some examples of how migrations could be extended with plugins. A lot of the examples she gave were based on what has been implemented in Hobo. There were some interesting points in the talk although my concentration levels were frayed by this point.

h3. Group Discussion

The main point of discussion was views. I think everyone was in agreement that something needs to change with the way Rails handles it’s views. I personally believe that they should be purposefully limiting so that it is hard to do bad things in the view. I am looking forward to seeing views taken to the next level and believe that there will be some large changes before 2.0 based on the comments during this discussion.

h3. After Party

The guys at “Skills Matter”:http://www.skillsmatter.com put on the first 100 drinks at “Liquid” – a Japanese-themed bar. We had a chance to mingle and discuss a few of the points that we forgot to ask after the talks.

Thanks to all the speakers, all in all a great day out.

Videos of some of the talks are now “available online”:http://skillsmatter.com/menu/479. Also, my colleague “Andy”:http://darkliquid.co.uk has blogged about his take on the day: “Part 1″:http://darkliquid.co.uk/2007/2/10/ruby-on-rails-exchange, “Part 2″:http://darkliquid.co.uk/2007/2/11/ruby-on-rails-exchange-part-2.