About Books Credits Photos Software Rumblings Travelling Home
Standalone ActiveRecord and tests

I’m not using Rails. I don’t have to, so I never got into the swing of it.

I do use ActiveRecord though. And quite a bit too.

rutema is using AR in order to get all those test results into the database. As the system is evolving and we’re passing into the phase where we concentrate on report generating and adding functionlity that gives you value out of all that stored data the tests for that code are starting to become cumbersome.

I find myself in sore need of the whole migrations/fixtures mechanism of Rails, only not for Rails.

The migrations structure was the first thing I missed: defining in a YAML file the model and subsequent versions of it and having the code to “up” the database. I solved that by coding the migrations myself and it’s not that big a deal because we don’t migrate so much anyway (we actually have not changed schema…yet).

Testing is different. Tests are mutliplying, they evolve, start small, grow, become more complex and require lots of data.

Hand coding all that data is a pain. It also in many cases defeats the DRY principle (I only need the same run entries to test the different reporters and the history function etc.)

Something that can be easily required in your unit tests and gives you a way to populate a test database with data loaded from a YAML or CSV file would be of immense help here. From there you get meaningfull model classes to base your tests on.

Since we are talking unit tests, using a SQlite3 :memory: database is probably a good idea (got the idea from _why’s Mosquito ). A lot faster than using your normal DB connection too.

But that means we need a nifty way to replace DB connections. So two things:

Time to take apart the Rails code and isolate the fixture generation.


A couple of hours later…

Turns out I don’t really have to isolate anything, just use what comes with AR. Another case for the RTFM history logs.

Although unfortunately, it doesn’t work as advertised outside of Rails. There is some Rails magic setting configuration which is checked by the fixtures code.

BUT, and this is where things get interesting, fixtures where not the way to go. This article by James Mead explains it much better than I can.

There was a small conversation about fixtures in the ruby-talk mailing list and I can say I fully agree with the points made in that blog post (still I am curious though, why couldn’t I get those fixtures to work).

The result is, that I used this approach (again described by James) and I got where I wanted.

A few caveats though:

I got bitten by forgetting that all assignment does in Ruby is pass the reference object around (that’s what happens when you code C and discuss C++ during the day and come back to Ruby during the night).

So defining a bunch of scenarios and assigning the to Run objects

scenarios=[]

scenarios<<Rutema::Model::Scenario.new(:name=>”TC001”,:status=>”success”)

scenarios<<Rutema::Model::Scenario.new(:name=>”TC003”,:status=>”error”)


r1=Rutema::Model:Run.new(:scenarios=>scenarios)

r2=Rutema::Model:Run.new(:scenarios=>scenarios)

resulted at the end in having the scenarios belonging to Run with id 2.

So I ended up building a few loops, which all things considered is a lot cleaner and compacter than defining the same thing in YAML (fixtures partially handles that with ERB code in the YAML file):


scenario_one_run1:

 name : "TC001" 

 status : "success" 

 run_id : 1

scenario_one_run2:

 name : "TC001" 

 status : "success"    

 run_id : 2

...

instead becomes:

1.upto(2) do |i|

steps=[]
steps&lt;&lt;Rutema::Model::Step.new(:name=&gt;"hard",:number=&gt;1,:status=&gt;"success",
:output=&gt;>"the first step is hard",:error=&gt;"",:duration=&gt;>1)
steps&lt;&lt;Rutema::Model::Step.new(:name=&gt;"easy",:number=&gt;2,:status=&gt;"success",
:output=&gt;"the next step is easy",:error=&gt;"",:duration=&gt;1)
scenarios&lt;&lt;Rutema::Model::Scenario.new(:name=&gt;"TC00#{i}",:version=&gt;"100",
:attended=&gt;false,:status=&gt;"success",
:start_time=&gt;(Time.now-10000i),:stop_time=&gt;(Time.now-10000i),
:steps=&gt;steps)
end

Notice that the steps are not defined in YAML above – that would have taken up too much space.