• Like
Testing orm based code
Upcoming SlideShare
Loading in...5
×

Testing orm based code

  • 750 views
Uploaded on

It is always a pain to test code with DB/ORM-dependencies. So, here the rules to ease the process ...

It is always a pain to test code with DB/ORM-dependencies. So, here the rules to ease the process ...

More in: Technology , Business
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads

Views

Total Views
750
On Slideshare
0
From Embeds
0
Number of Embeds
2

Actions

Shares
Downloads
0
Comments
0
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Testing ORM base codeViktor TurskyiCTO at WebbyLabKiev 2012
  • 2. Bad NewsORM-based code is databasedependent codeORM framework is a blackbox
  • 3. Good newsORM is about objects and classesWe can do testing without objectpersistency
  • 4. Rule 1: No tests for simple classespackage 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 owntests.
  • 5. 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 { ... }
  • 6. 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 );
  • 7. 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 );
  • 8. But still a lot of logic requires DBSo, bad news again:In complex operation injection is not alwayssuitable due to complex dependenciesUsing of DBI mock objects is not a good waydue to blackbox nature of the ORM frameworkOne Data Base for whole Model
  • 9. Simple solutionJust to use the same predefined set of data forall testcountries, users, companies, banks, materials, products, customers, partners,vats, stocks, languages, currencies,currency rates, units... and a lot moreShared set of predefined data worked until westarted work on aggregated reports. Everyreport require a new set of test data whichbreaks other tests data.
  • 10. Rule 3: Individual test environmentfor each tests setRecreate 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 :(
  • 11. 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();
  • 12. ConclusionsRule 1: No tests for simple classesRule 2: Try inject dependenciesRule 3: Individual test environment for each test
  • 13. 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
  • 14. Viktor Turskyiviktor@webbylab.com http://koorchik.blogspot.com http://search.cpan.org/~koorchik/ https://github.com/koorchik WebbyLab http://webbylab.com