testing orm based code

14
Testing ORM base code Viktor Turskyi CTO at WebbyLab Kiev 2012

Upload: viktor-turskyi

Post on 13-Jul-2015

829 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: Testing orm based code

Testing ORM base code

Viktor TurskyiCTO at WebbyLab

Kiev 2012

Page 2: Testing orm based code

Bad News

ORM-based code is database dependent code

ORM framework is a blackbox

Page 3: Testing orm based code

Good news

ORM is about objects and classes

We can do testing without object persistency

Page 4: Testing orm based code

package Language;

use base qw(Rose::DB::Object);

__PACKAGE__->meta->setup(

table => 'languages',

columns => [

language_id => { type => 'varchar', length => 3, not_null => 1 },

name => { type => 'varchar', length => 64, not_null => 1 },

],

primary_key_columns => [ 'language_id' ],

);

We do not test ORM framework - it has own tests.

Rule 1: No tests for simple classes

Page 5: Testing orm based code

Rule 2: Try inject dependenciespackage VAT;

use base qw(Rose::DB::Object);

__PACKAGE__->meta->setup(

table => 'vats',

columns => [

vat_id => { type => 'varchar', length => 16, not_null => 1 },

rate => { type => 'integer', not_null => 1 },

],

primary_key_columns => ['vat_id'],

);

sub netto2vat { ... }

sub brutto2vat { ... }

Page 6: Testing orm based code

Real life VS tests

# In real lifemy $vat = VAT->new(vat_id => 'VAT_20')->load();

my $vat_amount = $vat->netto2vat(102.51);

...

# In testsmy $vat_obj =VAT->new( rate => 20 );

is( $vat_obj->brutto2vat(123.01), '20.50', 'Checking brutto2vat calculations' );

is( $vat_obj->brutto2vat(456.00), '76.00', 'Checking brutto2vat calculations' );

is( $vat_obj->netto2vat(102.51), '20.50', 'Checking netto2vat calculations' );

is( $vat_obj->netto2vat(380.00), '76.00', 'Checking netto2vat calculations' );

Page 7: Testing orm based code

Real life VS tests (complex)

# In real lifemy $fin_event = FinEvent->new(

amount => 102.22,

# vat_object => ???

)->load();

my $amount = $fin_event->calculate_vat_amount();

# In testsmy $fin_event = FinEvent->new(

vat_object => VAT->new( rate=>20 ),

amount => 102.22

);

is( $fin_event->calculate_vat_amount(), 20.44, 'VAT calculation' );

Page 8: Testing orm based code

But still a lot of logic requires DB

So, bad news again:

In complex operation injection is not always suitable due to complex dependencies

Using of DBI mock objects is not a good way due to blackbox nature of the ORM framework

One Data Base for whole Model

Page 9: Testing orm based code

Simple solution

Just to use the same predefined set of data for all test countries, users, companies, banks, materials, products, customers, partners, vats, stocks, languages, currencies,currency rates, units... and a lot more

Shared set of predefined data worked until we started work on aggregated reports. Every report require a new set of test data which breaks other tests data.

Page 10: Testing orm based code

Rule 3: Individual test environment for each tests set

Recreate database for each tests set?

=> It takes too much time :(

Use embedded DB like SQLite (you can copy file)?

=> It requires support of two database schemas :(

Manually delete data after each test?

=> It requires additional cleanup procedures :(

Page 11: Testing orm based code

What we do?

Just revert transaction after test :)

use Test::More;

my $db = Rose::DB->new_or_cached();

$db->begin_work();

prepare_test_data();

do_testing();

$db->rollback();

Page 12: Testing orm based code

Conclusions

Rule 1: No tests for simple classes

Rule 2: Try inject dependencies

Rule 3: Individual test environment for each test

Page 13: Testing orm based code

Take a look at our testsmy $t = Test::Project->new(); # starts new transaction

$t->standard_setup()

->add_material_from_partner( 1200 )

->add_service_from_partner( 600, {date=>'2011-10-11'} )

->add_material_sale(60, {currency_rate=>800, currency_id=>'USD'})

;

iterate_test_data( 'report_a', sub {

my $data = shift;

my $report = Report::A->new(%{ $data->{input} });

$t->test_correct_report($report, $data);

});

# on $t->DESTROY() - will revert transaction

Page 14: Testing orm based code

Viktor [email protected]

http://koorchik.blogspot.com

http://search.cpan.org/~koorchik/

https://github.com/koorchik

WebbyLab

http://webbylab.com