Ian Docherty wrote:
Kate Yoak wrote:
Hi there,


Here is a newbie question:

I like to test my functionality in bits and pieces as I write it.  How
do I go about getting myself the context object in a test script?

For example, one of the tests catalyst installs is t/model_App.t where
it loads the model.  I'd love to then be able to use the model the same
way a controller would:

my $acc = $c->model('Account')->find(1);

Instead, I am doing
my $model = MyApp::Model::App->new();
my $acc = $model->recordset('Account')->find(1);

In addition to being less than ideal because I am doing something
different that I expect real application code to do, it presents
configuration problems.  Like, turns out, in order for config to take
effect, I have to run __PACKAGE__->setup; after configuring it - which
won't be necessary, I think, in a real app.

Is the practice of unit tests that break up the layers of catalyst
frowned upon? Or what should I do to make it work right?
Best practice is to keep your model separate from Catalyst so that you can (for example) create batch scripts or cron
jobs that work on the model without having to load the whole of Catalyst

Your model then would be in something like MyApp::Storage and accessed by your tests as so...

my $schema = MyApp::Storage->connect(
   'DBI:mysql:host=localhost;database=my_database',
   'username',
   'password',
   { 'mysql_enable_utf8' => 1 },
   {on_connect_do =>[ 'set names utf8' ] }
);
my $acc = $schema->resultset('Account')->find(1);

You would be testing your database layer separately from Catalyst.
Models aren't necessarily database layers.

I'd suggest that with a sufficiently rich Catalyst application, the 'model' as known by Catalyst could easily become an effective 'controller' of a further model. That secondary controller is going to want to load model classes, and Catalyst provides a nice way of controlling instantiation.

e.g.

sub handle_payment : Path {
  my ($self, $c, @args) = @_;

  my $proc = $c->model('PaymentProcessor');
my $result = $proc->pay( { currency => $args[0], amount => $args[1], id => $args[2] } );

  $c->stash->{result}->{message} = $result->message;
  $c->stash->{result}->{code} = $result->code;
}

# then, within PaymentProcessor
sub pay {
   my ($self, $args) = @_;

   MyApp->model( 'Ledger' )->insert( { # blah } );
   MyApp->model('Order')->update( { # blah } ) ;
}

This definitely does tightly couple the PaymentProcessor to the application. However, it also allows for better whitebox testing. If your logic is embedded within a Catalyst controller, the only sensible way to test it is to make an HTTP request. If it's in the model, the individual steps can be tested.

FWIW, the principle here is "Catalyst controllers should do nothing except validate and translate HTTP parameters to 'model' parameters. They shouldn't manipulate model objects, or build complicated logic by combinations of model calls".

Regards,
David


_______________________________________________
List: [email protected]
Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/[email protected]/
Dev site: http://dev.catalyst.perl.org/

Reply via email to