Thursday, August 31, 2006

Apple releases Launchd as Open Source

Apple has released their launchd process manager under the Apache Open Source License (version 2.0)


Launchd combines most of the features of the venerable Unix cron and init facilities. You can use launchd to make sure a process is always running ("watchdogging"), to run jobs at specific times, to run jobs when/if a file appars in a specified directory, etc.

I cover the basics of launchd in my book, "Unix for Mac OS X Tiger", and ars technicha covers it in their Tiger review.

There is already a FreeBSD port of launchd.

Macscripter.net has an article describing how to use launchd with AppleScript to access a Flashdrive.

Sunday, August 20, 2006

Mock Classes in Perl Unit Tests

UPDATE:
November 2, 2006: I finally released the Wrapper module described here on the CPAN system.

Today I added unit tests to a Perl module that uses the DBI module. My module, DBIx::Wrapper::VerySimple is one that I wrote years ago.

The tests use three tiny mock classes that take the place of the real DBI module. This allows the units tests to run in complete isolation from the actual DBI module.

Back when I wrote DBIx::Wrapper::VerySimple wasn't experienced enough to bother creating unit tests for my code. These days I create unit tests any time I create a new module for a project and sometimes for scripts as well.

The challenge in this case was that constructor method, DBIx::Wrapper::VerySimple->new() calls DBI->connect(), and I didn't want my unit tests to require connecting to an actual database. My solution is to have three tiny mock classes in my test code including a DBI.pm that the tests load before DBI::Wrapper can load the real DBI.pm.

Here's what my mock DBI.pm looks like:

# Mock class - for testing only
package DBI;
use strict;
use warnings;

# warn 'Loading mock library ' . __FILE__;
my $MOCK_DBH_CLASS = 'DBI::Mock::dbh';

my %ARGS = ();

sub connect {
my ( $class, @args ) = @_;
my
$fake_dbh = {};
bless $fake_dbh, $MOCK_DBH_CLASS;
$
ARGS{$fake_dbh} = \@args;
return
$fake_dbh;
}

1;




My test script loads the mock DBI.pm and two other mock classes before DBI::Wrapper is loaded for testing. This way, when DBI::Wrapper is compiled my mock DBI.pm is used instead of the real one:

# Ensure that DBI::Wrapper loads our mock DBI.pm
use
lib "$Bin/mock_lib";
use DBI;
use
DBI::Mock::dbh;
use
DBI::Mock::sth;


This approaches works very well, and I was able to create unit tests that intercept all the calls that DBI::Wrapper makes that would normally go to the real DBI, and my intercepting these I can check that DBI::Wrapper is passing the expected values to DBI. I am deliberately not testing the real DBI module which has its own extensive set of unit tests. I merely test that my module will make the expected calls to DBI.


More about DBI::Wrapper

The module is available online at http://www.matisse.net/perl-modules/DBI/Wrapper/
The version with the new unit tests is version 0.04.

From the README:

DBI::Wrapper is a simple module that provides a high-level interface
to the Perl DBI module. The provided methods are for fetching
a single record (returns a hash-ref), many records (returns
an array-ref of hash-refs), and for executing a non-select statement
(returns a result code).

The intention here is that your application will have much cleaner code,
so instead of writing:

$sql = 'SELECT name,address FROM $table WHERE zipcode=?';
$sth = $dbh->prepare($sql);
$rv = $sth->execute($zipcode);
@found_rows;
while ( my $hash_ref = $sth->fetchrow_hashref ) {
push( @found_rows, $hash_ref );
}

You would write:

$sql = 'SELECT name,address FROM $table WHERE zipcode=?';
$found_rows = $wrapper->FetchAll($sql,$zipcode); # An array_ref of hash_refs


I wrote the my version of DBI::Wrapper several years ago after I got the idea from a co-worker when I was doing a gig at TechTV.