Tuesday, October 7, 2008

Testing for database insert with ActiveRecord

Often in my tests, I want to test to see whether a database insert occurred; more often I want to make sure that an insert did not happen. I've found a few approaches using mocking, neither of which I was very satisfied with.

Mocking AR's connection's insert


This approach won't win you any code cleanliness awards, but when you absolutely must tell whether or not your code is trying to insert records, this will get the job done.

def test_that_fails_if_any_inserts_happen
flexmock( ActiveRecord::Base.connection ).should_receive( :insert ).never

# code to test
end


This doesn't make an assertion, but flexmock is setting an expectation that the method insert will never be called on AR's connection object before flexmock's teardown gets called.

Mocking model's create


This is a little higher level, but even more deeply coupled with your code.

def test_that_some_model_never_creates
flexmock( MyModel ).should_receive( :create, :create! ).never

# code to test
end

Doing the test this way seems a little cleaner at first, but there's one major caveat: create and create! are only two avenues to doing an insert in the database; this test would pass if the code was refactored to make a raw sql insert, if it used ar-extension's import method, etc. You could remember to change the test if either of those things were to ever happen, but it's best to just avoid this unless you have few options.

How does ActiveRecord test this?


After being unsatisfied with the last two ways to test this, I got the bright idea to look at AR's tests and see how they test this. Surely they need to test whether objects get created, right? Right. This is taken from AR 2.1.1's finder_test.rb, lines 598-604:

def test_find_or_create_from_one_attribute
number_of_companies = Company.count
sig38 = Company.find_or_create_by_name("38signals")
assert_equal number_of_companies + 1, Company.count
assert_equal sig38, Company.find_or_create_by_name("38signals")
assert !sig38.new_record?
end

As you can see, to test whether a database record is created, they take the count of the table before and after the code they're testing, and make sure the final count matches their expectation. Simple.
I was a little underwhelmed by their solution. It's clean, but it's lacking something that I can't put my finger on. Overall. I like their way of testing this over my two solutions, so I've started refactoring my tests to work this way.

Has anyone out there run into this problem, and come up with a better solution?

Tuesday, September 30, 2008

Running a subset of shoulda tests from the command line

Someone finally revealed a solution for something bothering me for a long time: how to run a subset of shoulda tests. This should help a lot for those really ugly bugs where my last resort is to look at the logs and see exactly what's going on.

Friday, August 1, 2008

Apparently code order matters in Ruby

I was recently working on some code that had a structure like this:

module OuterModule
class MyObject
include InnerModule
end

module InnerModule
def hello
"hello"
end
end
end

include OuterModule

m = MyObject.new

puts m.hello

When I ran it, I would get this unfortunate output:

test_ruby_class_heirarchy.rb:3: uninitialized constant OuterModule::MyObject::InnerModule (NameError)

I'm too embarrased to list all the voodoo I tried to get this to work, but simply putting the definition of MyObject below that of InnerModule did the trick:

module OuterModule
module InnerModule
def hello
"hello"
end
end

class MyObject
include InnerModule
end
end

include OuterModule

m = MyObject.new

puts m.hello

This gives me the expected output:

hello

Going into this, I would have never guessed that the order in which I defined things would have this effect.

Friday, July 11, 2008

A stupid reason I like shoulda

I've been interested shoulda for a while but hadn't played around with it until today. I really like the idea of seperate contexts and not having to name methods stuff like "test_process_should_create_record_if_one_doesnt_exist". With some playing around though, I found out that shoulda helps me work around a bug I found in flexmock and rails where the teardown method wasn't getting called correctly in my tests.

As an addendum to that mailing list post, I get this output:

Loaded suite test/unit/test_shoulda_test
Started
inside setup
inside test
inside teardown
.
Finished in 0.069689 seconds.

1 tests, 1 assertions, 0 failures, 0 errors

... when I run this code:

require File.join( File.dirname(__FILE__), '..', 'test_helper' )
require 'test/unit'
require 'flexmock/test_unit'

class TestTest < Test::Unit::TestCase
context "This test" do
setup do
$stderr.puts "inside setup"
end

teardown do
$stderr.puts "inside teardown"
end

should "pass" do
$stderr.puts "inside test"
assert true
end
end
end

Thanks shoulda!

Wednesday, July 9, 2008

Testing Tidbts #1: Be explicit

I was recently refactoring some tests (you do refactor your tests, right? hmm, sounds like an idea for another post..) and I came across something like this, trying to test whether a time field on a rails model was updated correctly:

assert_in_delta mq.ended_at,
Time.now,
1

What's that 1 for at the end? What's the third parameter for assert_in_delta, anyways?

If I had been more explicit, that might be a little more apparent:

assert_in_delta mq.ended_at,
Time.now,
1.second

It's a small change, and totally uneccessary in real, working code, but I feel like it improves the readability a great deal.

Monday, July 7, 2008

Awesome: Easy Join Table Conditions

Most of the new-in-edge-rails features elicit a yawn from me (at best), but this one caught my eye. Some of the projects at work that I assist on could be cleaned up a great deal with this functionality.

Tuesday, May 27, 2008

sphinx charset_table with unicode character folding

In a project at work I needed to have sphinx treat accented characters and their unaccented versions the same, eg: é is equivalent to e, etc. I took this list I found on the sphinx wiki and transformed it into a sphinx friendly charset_table.

Now when I do a search for the string "Héctor Lavoe" it matches the string "Hector Lavoe". Awesome!

Edit: fixed missing "
é" in first sentence.

Splitting long lines in text files with vim

Recently I needed to split long lines in a text file, and it took me a few minutes to figure out how to do this neatly in vim. In case I need it again, here's the regexp I used:

%s/\(.\{250\}[^ ]* \)/\1 \\^M/gc

250 is an arbitrary number I picked, and the "^M" part is a newline entered in vim by pressing ctrl-v, ctrl-m. Also, I split the line where there was a space, because I knew the data had frequent spaces and I didn't want to split a word.

Tuesday, March 25, 2008

ActiveRecord #find syntax and you

I've recently been adding eager-loading of ActiveRecord associations into a project at work, and I've run into a few problems because of how the original #find queries were wrtten. I did a little research, and found out how you can help out AR a bit and save yourself (and me) some hassle.

The easiest way to query AR is to simply use an id, like this:

User.find(10)

This uses this sql, nicely referenced by the table name:

SELECT * FROM users WHERE (users.`id` = 10)

The downside to this is that a plain #find call will throw an exception if no record exists, and that’s not good (unless caught, the client will see a 500 at that point).

We’ve been getting around this by using find calls like this:

User.find( :all, :conditions => ['id = ?', 10] )

Which generates this sql:

SELECT * FROM users WHERE (id = 10)

This way is good because it will return nil if no record is found, instead of throwing an exception. The subtle downside is that the string in the conditions parameter is copied directly into the sql string, because AR doesn’t know how to do anything with it.

If instead you use a hash for the conditions parameter, like this:

User.find( :all, :conditions => { :id => 10 } )

AR knows that you’re referencing a column from that model’s table, and gives you the safe sql with no bad exception aftertaste:

SELECT * FROM users WHERE (users.`id` = 10)

As an added bonus, because AR is allowed to do it’s magic, if you decide to pass it an array instead of a single integer, it can do the right thing without you having to change your #find code:

User.find( :all, :conditions => { :id => [10,12] } )

Which automatically comes out as:

SELECT * FROM users WHERE (users.`id` IN (10,12))

Summary:

Don’t do this:
User.find(10)
or this: User.find( :all, :conditions => ['id = ?', 10] ).

Do this:
User.find( :all, :conditions => { :id => 10 } )

Monday, March 17, 2008

Rescue Me!

With all the talk recently about elegant nil handling in Ruby (like andand and if_not_nil and #try), I was surprised that no one mentioned using rescue.

Surely every ruby programmer knows rescue when used with its friend begin, but did you know rescue has a statement modifier form?

For example, the following throws an exception:
>> [ "Bacon Lettuce Tomato", nil ].collect {|s| s.upcase.reverse[0,6].reverse }
NoMethodError: You have a nil object when you didn't expect it!
The error occurred while evaluating nil.upcase
from (irb):15
from (irb):15:in `collect'
from (irb):15

Adding rescue nil after that ugly bit of method chaining gets me this:
>> [ "Bacon Lettuce Tomato", nil ].collect {|s| s.upcase.reverse[0,6].reverse rescue nil }
=> ["TOMATO", nil]

... and rescue "" gets me this, useful if I want to return all the same types:
>> [ "Bacon Lettuce Tomato", nil ].collect {|s| s.upcase.reverse[0,6].reverse rescue "" }
=> ["TOMATO", ""]

I haven't used this other than playing around in a console, so I don't really know if this has nasty side effects or not. Maybe there's a reason all these Ruby gurus haven't mentioned it...