Category Archives: Test Driven Development (TDD)

MySQL Triggers w/Rails

I recently incorporated db-triggers with a Rails app to maintain some counts that were otherwise fairly expensive to retrieve.  Rails wasn’t super-pumped about the idea (what with the “keep all the logic in the app” approach and all), but sometimes… you know… you know better than your framework.

Some things I was aiming for:

  1. Set them up with normal migrations.
  2. Test them with the normal test suite/normal fixtures.
  3. Make recovery/reset simple for when the table (inevitably) is somehow out of sync.

The “frequent counts” table

I’ll have multiple counts but not TOO many — enough that I don’t want to have a column per count but not enough that I mind using “LIKE” to lookup patterns, so my table has: id, code, current_count.

Code will be a unique key (important later) and be formatted like “style_ABC_size_456”.

So, when a new item is added it’ll be associated with a style and some sizes – each combination will either need to be setup (with a current_count = 1) or an existing combo will be found and +=1.

The FrequentCount class has the fairly straightforward finders that you’d expect + methods to reset each of the counts that it contains.  The reset methods follow the pattern “reset_frequent_count_COUNT_NAME” -> they clear the existing counts that they maintain before repopulating them.

I also threw in a reset_all method that looks for anything on the class following the “reset_frequent_count_COUNT_NAME” pattern and runs them.

The trigger-SQL

The SQL for creating the triggers will be needed by the migration as well as the test suite.  In fact, the test suite will need to run them somewhat often due to the way the standard tests “prepare” the database.

I ended up throwing it in lib/trigger_sql.rb.  Methods there are named with the pattern “sql_for_TABLE_OPERATION_TRIGGER_NAME” ex: sql_for_items_insert_style_and_size

Many of the triggers could not rely on pre-existing rows.  i.e. a new style/size combination needs to INSERT where an existing combo could update ( +=1 ).  To get around this, I relied on the unique key setup earlier on the “code” field for the frequent counts table.  <– that allowed me to lean on insert statements with “ON DUPLICATE KEY” clauses with update statements.  Something like this…
create trigger items_insert_style_and_size after insert on items
for each row
begin
insert into frequent_counts(code, current_count)
values (concat(‘style_’, new.style, ‘_size_’, new.size), 1)
on duplicate key update current_count = current_count + 1;
end;
The Migration

I’ve already given away most of the fun stuff about the migration.  It just needs to run through the triggers that are being setup at this specific time, doing things like:
TriggerSql.connection.execute(TriggerSql.sql_for_items_insert_items_by_style_and_size)
and then make sure to populate it all (with that reset_all) method when we’re done. <– next time out I may want to call specific methods to reset just the ones I care about but this first time, I can just do the whole table.

Testing with Fixtures
Rails goes a little too far when running the default test tasks for us – it ends up nuking the triggers on us, but not to fear: it’s a quick hack in the Rakefile.
I’m going to spare you some details (drop me a line if you want them) but I basically overrode the db:test:prepare method to call a special version of the clone_structure task.  My version has a dependent task that does:
# find methods that follow our pattern of “methods providing trigger sql” and execute the contents of each
TriggerSql.methods.select{ |m| m =~ /sql_for_.+/ }.each do |method_name|
ActiveRecord::Base.connection.execute(TriggerSql.send(method_name))
end
As you see there, it’s leaning on that naming convention “sql_for_TABLE_OPERATION_TRIGGER_NAME” to find the sql to (re)apply.
That’s it!
Migrations set them up and share the code to do so with the fixtures that can repeat the tests whenever we need.  Those reset methods also come in handy not only for the initial population (by the migration) but we can call them manually should we need them.

Leave a comment

Filed under deployment, rails, Ruby, SQL, Test Driven Development (TDD)

Receiving Email with ActionMailer

This really was a snap.

A nice and simple testing recipe (#68) demonstrates how to read in an email from a fixture in just a few lines and pass them to your processing method (MailReceiver.receive in this case).

def read_fixture(action)
IO.readlines(“#{FIXTURES_PATH}/mail_receiver/#{action}”)
end

def test_something
email = read_fixture(“junk_mail”).join
MailReceiver.receive(email)

# assertions
end

And then a few lines on how to feed it “for real”. and we’re up and running!

The only catch… there was some anger over the parens:

mailman: "|/path/to/app/script/runner Mailman.receive(STDIN.read)"

We ended up escaping them, but according to those directions I just linked to – you can also just quote the call to receive.


			

Leave a comment

Filed under Email, rails, Ruby, Test Driven Development (TDD)

Be careful with assert_not_equal

As I discovered today, assert_not_equal has a pretty big problem: it does exactly what you ask it to do.

I just need to make sure that a user has at least one record:

assert_not_equal 0, UserPreference.find_by_user_id(user_id)

As you may have just caught: that is always going to pass. Not a tough mistake when you’re motoring along and not putting too much thought into something so simple: The finder isn’t going to return the count of matching rows… it’s either going to return a populated object or nil, neither of which will be equal to 0.

Fixing the bug is trivial, but lesson learned:

From now on whenever I write assert_not_equal – I am going to make sure it works by temporarily changing it to assert_equal and taking a look at the error message.

In this case: if the error message said something like “<0> expected but was <2>”, I would know that things are working.

Whereas if the error said “<0> expected but was <#<UserPreference…>>”, I’d know right away that I need to fix it up (and have one more cup of coffee).

2 Comments

Filed under rails, Ruby, Test Driven Development (TDD)

Adding tests to rake

We’ve developed a few batch applications that live in the same directory structure as the Rails app they’re intimate with. So, the lib/foo/test/ contains some tests for the ‘foo’ batch app. lib/bar/test has the ones for ‘bar’ etc.

It was kind of annoying to have seperate rake files though – or worse to need to execute each of the batch unit tests seperately.

Since the number of tests required is quite small at this point – they all just live directly under the test dir (not broken out into unit, functional, etc)…

desc ‘Test the batch stuff.’
Rake::TestTask.new(:test) do |t|

t.pattern = ‘lib/**/test/*_test.rb’
t.verbose = true

end

Nice and easy again.

Leave a comment

Filed under rails, Ruby, Test Driven Development (TDD)

No help from the log

Upon synching up, a colleague noticed that I had a test failing… kinda odd since I've been (trying to be) pretty good about keeping things up to date. Running them myself, we noticed that I was actually experiencing a quiet error message that was preventing my unit tests from running at all. I had been seeing the familiar "0 failures, 0 errors" message for the functional tests and not really noticing that there was no message at all for my unit tests.

Running a trace (and paying attention now 🙂 ) it seemed to be experiencing an error executing the integration tests.

The helpful error message? … rake aborted!

Can you expand on that? … Test failures

oh, thanks!

After a good hour of banging my head against the wall I modified the rakefile's default task:
task :default => [:test_units, :test_functional]

Now, the rerun actually gave me a decent error!
Mysql::Error: Table 'tags' already exists: CREATE TABLE tags (`id` int(11) DEFAULT NULL auto_increment PRIMARY KEY, `code` int(11), `name` varchar(255)) ENGINE=InnoDB

10 seconds later I had dropped the table – let the rake recreate it itself – and everything was fine! Would have been nice if I didn't have to hold it's head underwater to get that information out of it!

Leave a comment

Filed under rails, Ruby, Test Driven Development (TDD)

Requiring and Testing Cookies

Requirement: Users must have cookies enabled to use the site. If they don't: give them some sort of graceful warning to let them know there's going to be trouble. Doesn't sound too bad… it ended up being much more of an adventure than I had expected.

Step 1. Add the cookie requirement

After a bit of deliberation I decided that I would rather not use Javascript (if I didn't have to). The trouble with testing for cookies with _only_ server side code is… well, you are _only_ on the server side – and 'cookies enabled' isn't exactly a server setting 🙂 . When setting a cookie value – you are merely setting it in a hash to be persisted later (when the request finishes processing) – therefore you won't really know if it is going to _work_ in the browser until… well… you actually try to set it in the browser.

As you likely know, rails uses a cookie by default to track the session id (cookie id is :_session_id). So, it seems easy enough that I can verify that cookie is there to determine if cookies are enabled.

The catch, of course, is that on the first request: my code will check if the cookie is present… see that it isn't (the browser didn't _already_ have it) and conclude that cookies are disabled! That's not going to work: in all likelihood they are enabled… it's just a matter of me not getting a chance to set the thing yet.

So, I'm going to drop in a little workaround:

First check for the _session_id cookie. If it's there: great, we're good to go. Mission accomplished: proceed to the requested functionality with my blessings.
If not, what do we know? Not much. We know that the cookie hasn't _already_ been added, but we don't know if that's because it's actually been blocked… or have we just not even attempted it yet? What's the easiest way to determine if this is the first time the application has been accessed? Personally, I think it's to not even try to figure it out and just make another call: when that call is fielded you _know_ it's not the first!

So, we add a 'special' parameter and repeat the users request. The subsequent request will be able to check not only for the presence of our cookie but also for the presence of our special parameter – if both turn up negative: we can conclude that cookies are disabled.

before_12345.png
Step 2. Add the unit tests

Alright, so all my functional tests are suddenly failing since they are all being blocked by the cookie requirement! I need to add my simple test for the code I just added – while also modifying the existing tests to have proper credentials to access the app. No prob, right? I already have my tests jumping through a little hoop to properly 'login', so now in addition to the "authenticate" stuff I already have: I just add a piece of code to add my cookie:
first_cookie_test.png

Run the tests… trouble. The cookie isn't being found. This does make some sense: what does "cookies" really mean to my test class? So, I change things so that instead of saying cookies[:_session_id], I say:

final_cookie_test.png

Now this works for the test… but now it isn't working in the actual web app!

Step 3. Making it WorkI suppose I could drop in another piece of code to have my filter check both cookies and @request.cookies but that's no good. I need to use @request.cookies for both test and 'application' code.
The final snags…

a. @request.cookies won't play nice with my symbol ( :_session_id ), so I need to actually use a string for the key ("_session_id").

b. @request.cookies["_session_id"] is returning a Hash and is also returning blank instead of null if not found

So… without further adu:

The test code:

final_cookie_test.png

The app code:

after_12345.png

As with everyting Rails: the end solution is so simple it's painful.

And they lived happily ever after, The End.

6 Comments

Filed under Cookies, rails, Ruby, Test Driven Development (TDD)

Testing the controller

A couple things I found very handy today while setting up a functional test for a controller.

First off, this controller relies on a user being logged in (but this isn't the controller that actually manages the login process).

The easiest way I found to make sure that I was properly logged in was to temporarily change @controller to the UserController — login — then change back. Like this:

controllerswap.png

A handy pointer regarding testing instance variables from Jeremy Kemper.

To access an instance variable in the controller that you are testing: simply examine what would have been sent to the view, in the response:

something = @response.template.assigns['something'] 

Leave a comment

Filed under Agile Software Dev, rails, Ruby, Test Driven Development (TDD)