From 7f73515b0bdbea84e81b961354410e49cbbebee2 Mon Sep 17 00:00:00 2001 From: Olli-Antti Kivilahti Date: Wed, 25 Mar 2015 15:53:07 +0200 Subject: [PATCH] Bug 13906 - TestObjectFactory(ies) for Koha objects. Enable easy Test object creation from HASHes or from preconfigured test groups. Doing tests should be easy. One biggest hindrance in doing tests is the difficulty of doing so in Koha. We use a lot of hard-coded SQL, which occasionally requires quite a bit of fiddling to get it right. Satisfying complex object dependency chains is hard. For example, testing Overdue notice sending, one must create -letters -overuderules -circulationrules -borrowers -biblios -items -issues (checkouts) ... Also one must take care to clean up the database modifications afterward to make encapsulated tests which don't leak over other tests. This is especially so for front-end testing. TestObjectFactories significantly alleviate this pain, by offering preconfigured test object groups or enable creating test objects on demand. They try to recover from errors, like when the previous test crashed and left testing objects into the DB, preventing adding them again. Also they store what object they created to 3 different levels of stashes to facilitate complex test configurations, and they have the dignity to clean-up the test contexts after testing has happened (if the script doesn't crash)! USAGE: use t::db_dependent::TestObjects::Borrowers::BorrowerFactory; %#Setting up the test context my $testContext = {}; my $password = '1234'; my $borrowerFactory = t::db_dependent::TestObjects::Borrowers::BorrowerFactory->new(); my $borrowers = $borrowerFactory->createTestGroup([ {firstname => 'Olli-Antti', surname => 'Kivi', cardnumber => '1A01', branchcode => 'CPL', flags => '1', #superlibrarian, not exactly a very good way of doing permission testing? userid => 'mini_admin', password => $password, }, ], undef, $testContext); %#Test context set, starting testing: eval { ... #Run your tests here }; if ($@) { #Catch all leaking errors and gracefully terminate. warn $@; tearDown(); exit 1; } %#All tests done, tear down test context tearDown(); done_testing; sub tearDown { t::db_dependent::TestObjects::ObjectFactory->tearDownTestContext($testContext); } --- .../TestObjects/Biblios/BiblioFactory.pm | 96 +++++++++++++ .../TestObjects/Biblios/ExampleBiblios.pm | 75 ++++++++++ .../TestObjects/Borrowers/BorrowerFactory.pm | 151 +++++++++++++++++++++ .../TestObjects/Borrowers/ExampleBorrowers.pm | 64 +++++++++ t/db_dependent/TestObjects/Issues/ExampleIssues.pm | 92 +++++++++++++ t/db_dependent/TestObjects/Issues/IssueFactory.pm | 151 +++++++++++++++++++++ t/db_dependent/TestObjects/Items/ExampleItems.pm | 108 +++++++++++++++ t/db_dependent/TestObjects/Items/ItemFactory.pm | 104 ++++++++++++++ .../LetterTemplates/ExampleLetterTemplates.pm | 92 +++++++++++++ .../LetterTemplates/LetterTemplateFactory.pm | 101 ++++++++++++++ t/db_dependent/TestObjects/ObjectFactory.pm | 108 +++++++++++++++ 11 files changed, 1142 insertions(+) create mode 100644 t/db_dependent/TestObjects/Biblios/BiblioFactory.pm create mode 100644 t/db_dependent/TestObjects/Biblios/ExampleBiblios.pm create mode 100644 t/db_dependent/TestObjects/Borrowers/BorrowerFactory.pm create mode 100644 t/db_dependent/TestObjects/Borrowers/ExampleBorrowers.pm create mode 100644 t/db_dependent/TestObjects/Issues/ExampleIssues.pm create mode 100644 t/db_dependent/TestObjects/Issues/IssueFactory.pm create mode 100644 t/db_dependent/TestObjects/Items/ExampleItems.pm create mode 100644 t/db_dependent/TestObjects/Items/ItemFactory.pm create mode 100644 t/db_dependent/TestObjects/LetterTemplates/ExampleLetterTemplates.pm create mode 100644 t/db_dependent/TestObjects/LetterTemplates/LetterTemplateFactory.pm create mode 100644 t/db_dependent/TestObjects/ObjectFactory.pm diff --git a/t/db_dependent/TestObjects/Biblios/BiblioFactory.pm b/t/db_dependent/TestObjects/Biblios/BiblioFactory.pm new file mode 100644 index 0000000..c099c68 --- /dev/null +++ b/t/db_dependent/TestObjects/Biblios/BiblioFactory.pm @@ -0,0 +1,96 @@ +package t::db_dependent::TestObjects::Biblios::BiblioFactory; + +# Copyright Vaara-kirjastot 2015 +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +use Modern::Perl; +use Carp; + +use C4::Biblio; + +use t::db_dependent::TestObjects::Biblios::ExampleBiblios; +use t::db_dependent::TestObjects::ObjectFactory; + +=head t::db_dependent::TestObjects::Biblios::createTestGroup( $data [, $hashKey] ) +Calls C4::Biblio::TransformKohaToMarc() to make a MARC::Record and add it to +the DB. Returns a HASH of MARC::Records. +The HASH is keyed with the biblionumber, or the given $hashKey. +The biblionumber is injected to the MARC::Record-object to be easily accessable, +so we can get it like this: + $records->{$key}->{biblionumber}; + +See C4::Biblio::TransformKohaToMarc() for how the biblio- or biblioitem-tables' +columns need to be given. +=cut + +sub createTestGroup { + my ($biblios, $hashKey) = @_; + my %records; + foreach my $biblio (@$biblios) { + my $record = C4::Biblio::TransformKohaToMarc($biblio); + my ($biblionumber, $biblioitemnumber) = C4::Biblio::AddBiblio($record,''); + $record->{biblionumber} = $biblionumber; + + my $key = t::db_dependent::TestObjects::ObjectFactory::getHashKey($biblio, $biblionumber, $hashKey); + + $records{$key} = $record; + } + return \%records; +} + +=head + + my $records = createTestGroup(); + ##Do funky stuff + deleteTestGroup($records); + +Removes the given test group from the DB. + +=cut + +sub deleteTestGroup { + my $records = shift; + + my ( $biblionumberFieldCode, $biblionumberSubfieldCode ) = + C4::Biblio::GetMarcFromKohaField( "biblio.biblionumber", '' ); + + my $schema = Koha::Database->new_schema(); + while( my ($key, $record) = each %$records) { + my $biblionumber = $record->subfield($biblionumberFieldCode, $biblionumberSubfieldCode); + $schema->resultset('Biblio')->search($biblionumber)->delete_all(); + $schema->resultset('Biblioitem')->search($biblionumber)->delete_all(); + } +} +sub _deleteTestGroupFromIdentifiers { + my $testGroupIdentifiers = shift; + + my $schema = Koha::Database->new_schema(); + foreach my $isbn (@$testGroupIdentifiers) { + $schema->resultset('Biblio')->search({"biblioitems.isbn" => $isbn},{join => 'biblioitems'})->delete(); + $schema->resultset('Biblioitem')->search({isbn => $isbn})->delete(); + } +} + +sub createTestGroup1 { + return t::db_dependent::TestObjects::Biblios::ExampleBiblios::createTestGroup1(@_); +} +sub deleteTestGroup1 { + return t::db_dependent::TestObjects::Biblios::ExampleBiblios::deleteTestGroup1(@_); +} + +1; \ No newline at end of file diff --git a/t/db_dependent/TestObjects/Biblios/ExampleBiblios.pm b/t/db_dependent/TestObjects/Biblios/ExampleBiblios.pm new file mode 100644 index 0000000..f65db59 --- /dev/null +++ b/t/db_dependent/TestObjects/Biblios/ExampleBiblios.pm @@ -0,0 +1,75 @@ +package t::db_dependent::TestObjects::Biblios::ExampleBiblios; + +# Copyright Vaara-kirjastot 2015 +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +use Modern::Perl; +use Carp; + +use t::db_dependent::TestObjects::Biblios::BiblioFactory; + +=head createTestGroupX + + my $hash_of_records = TestObjects::C4::Biblio::ExampleBiblios::createTestGroup1(); + + ##Do stuff with the test group## + + #Remember to delete them after use. + TestObjects::C4::Biblio::ExampleBiblios::deleteTestGroup1($biblionumbers); +=cut + +my @testGroup1Identifiers = ('9519671580', '9519671581', '9519671582', '9519671583', + ); #So we can later delete these Biblios +sub createTestGroup1 { + my $records = [ + { + "biblio.title" => 'I wish I met your mother', + "biblio.author" => 'Pertti Kurikka', + "biblio.copyrightdate" => '1960', + "biblioitems.isbn" => $testGroup1Identifiers[0], + "biblioitems.itemtype" => 'BK', + }, + { + "biblio.title" => 'Me and your mother', + "biblio.author" => 'Jaakko Kurikka', + "biblio.copyrightdate" => '1961', + "biblioitems.isbn" => $testGroup1Identifiers[1], + "biblioitems.itemtype" => 'BK', + }, + { + "biblio.title" => 'How I met your mother', + "biblio.author" => 'Martti Kurikka', + "biblio.copyrightdate" => '1962', + "biblioitems.isbn" => $testGroup1Identifiers[2], + "biblioitems.itemtype" => 'DV', + }, + { + "biblio.title" => 'How I wish I had met your mother', + "biblio.author" => 'Tapio Kurikka', + "biblio.copyrightdate" => '1963', + "biblioitems.isbn" => $testGroup1Identifiers[3], + "biblioitems.itemtype" => 'DV', + }, + ]; + return t::db_dependent::TestObjects::Biblios::BiblioFactory::createTestGroup($records, 'biblioitems.isbn'); +} +sub deleteTestGroup1 { + t::db_dependent::TestObjects::Biblios::BiblioFactory::_deleteTestGroupFromIdentifiers(\@testGroup1Identifiers); +} + +1; diff --git a/t/db_dependent/TestObjects/Borrowers/BorrowerFactory.pm b/t/db_dependent/TestObjects/Borrowers/BorrowerFactory.pm new file mode 100644 index 0000000..1a611ba --- /dev/null +++ b/t/db_dependent/TestObjects/Borrowers/BorrowerFactory.pm @@ -0,0 +1,151 @@ +package t::db_dependent::TestObjects::Borrowers::BorrowerFactory; + +# Copyright Vaara-kirjastot 2015 +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +use Modern::Perl; +use Carp; +use Scalar::Util qw(blessed); + +use C4::Members; + +use t::db_dependent::TestObjects::Borrowers::ExampleBorrowers; + +use Koha::Exception::BadParameter; + +use base qw(t::db_dependent::TestObjects::ObjectFactory); + +sub new { + my ($class) = @_; + + my $self = {}; + bless($self, $class); + return $self; +} + +=head createTestGroup( $data [, $hashKey] ) + + my $borrowerFactory = t::db_dependent::TestObjects::Borrowers::BorrowerFactory->new(); + $borrowerFactory->createTestGroup([ + {firstname => 'Olli-Antti', + surname => 'Kivi', + cardnumber => '11A001', + branch => 'CPL', + }, + ], undef, $testContext); + +The HASH is keyed with the given $hashKey or 'koha.borrowers.cardnumber' +See C4::Members::AddMember() for how the table columns need to be given. + +@RETURNS HASHRef of $hashKey => $borrower-objects: +=cut + +sub createTestGroup { + my ($self, $objects, $hashKey, $featureStash, $scenarioStash, $stepStash) = @_; + $self->_validateStashes($featureStash, $scenarioStash, $stepStash); + $hashKey = 'cardnumber' unless $hashKey; + + my %objects; + foreach my $object (@$objects) { + + $self->validateAndPopulateDefaultValues($object, $hashKey); + + #Try to add the Borrower, but it might fail because of the barcode or other UNIQUE constraint. + #Catch the error and try looking for the Borrower if we suspect it is present in the DB. + my $borrowernumber; + eval { + $borrowernumber = C4::Members::AddMember(%$object); + }; + if ($@) { + if (blessed($@) && $@->isa('DBIx::Class::Exception') && + $@->{msg} =~ /Duplicate entry '.+?' for key 'cardnumber'/) { #DBIx should throw other types of exceptions instead of this general type :( + #This exception type is OK, we ignore this and try fetching the existing Object next. + } + else { + die $@; + } + } + + #If adding failed, we still get some strange borrowernumber result. + #Check for sure by finding the real borrower. + my $borrower = C4::Members::GetMember(cardnumber => $object->{cardnumber}); + unless ($borrower) { + carp "BorrowerFactory:> No borrower for cardnumber '".$object->{cardnumber}."'"; + next(); + } + + my $key = $self->getHashKey($borrower, $borrowernumber, $hashKey); + + $objects{$key} = $borrower; + } + + $self->_persistToStashes(\%objects, 'borrowers', $featureStash, $scenarioStash, $stepStash); + + return \%objects; +} + +=head validateAndPopulateDefaultValues +@OVERLOAD + +Validates given Object parameters and makes sure that critical fields are given +and populates defaults for missing values. +=cut + +sub validateAndPopulateDefaultValues { + my ($self, $borrower, $hashKey) = @_; + $self->SUPER::validateAndPopulateDefaultValues($borrower, $hashKey); + + $borrower->{categorycode} = 'PT' unless $borrower->{categorycode}; + $borrower->{branchcode} = 'CPL' unless $borrower->{branchcode}; + $borrower->{dateofbirth} = '1985-10-12' unless $borrower->{dateofbirth}; +} + +=head deleteTestGroup + + my $records = createTestGroup(); + ##Do funky stuff + deleteTestGroup($records); + +Removes the given test group from the DB. + +=cut + +sub deleteTestGroup { + my ($self, $objects) = @_; + + my $schema = Koha::Database->new_schema(); + while( my ($key, $object) = each %$objects) { + $schema->resultset('Borrower')->find($object->{borrowernumber})->delete(); + } +} +sub _deleteTestGroupFromIdentifiers { + my $testGroupIdentifiers = shift; + + my $schema = Koha::Database->new_schema(); + foreach my $key (@$testGroupIdentifiers) { + $schema->resultset('Borrower')->find({"cardnumber" => $key})->delete(); + } +} + +sub createTestGroup1 { + return t::db_dependent::TestObjects::Borrowers::ExampleBorrowers::createTestGroup1(@_); +} +sub deleteTestGroup1 { + return t::db_dependent::TestObjects::Borrowers::ExampleBorrowers::deleteTestGroup1(@_); +} +1; \ No newline at end of file diff --git a/t/db_dependent/TestObjects/Borrowers/ExampleBorrowers.pm b/t/db_dependent/TestObjects/Borrowers/ExampleBorrowers.pm new file mode 100644 index 0000000..4d235c4 --- /dev/null +++ b/t/db_dependent/TestObjects/Borrowers/ExampleBorrowers.pm @@ -0,0 +1,64 @@ +package t::db_dependent::TestObjects::Borrowers::ExampleBorrowers; + +# Copyright Vaara-kirjastot 2015 +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +use Modern::Perl; +use Carp; + +use t::db_dependent::TestObjects::Borrowers::BorrowerFactory; + +=head createTestGroupX + + You should use the appropriate Factory-class to create these test-objects. + +=cut + +my @testGroup1Identifiers = ('167Azel0001', '167Azel0002', '167Azel0003', '167Azel0004', + '167Azel0005', '167Azel0006', '167Azel0007', '167Azel0008', + ); + +=head createTestGroup1 +@RETURNS Reference to Hash of Borrowers with hash-keys as borrowers.cardnumber +=cut +sub createTestGroup1 { + my @borrowers = ( + {cardnumber => $testGroup1Identifiers[0], branchcode => 'CPL', categorycode => 'YA', + surname => 'Costly', firstname => 'Colt', address => 'Street 11', zipcode => '10221'}, + {cardnumber => $testGroup1Identifiers[1], branchcode => 'CPL', categorycode => 'YA', + surname => 'Dearly', firstname => 'Colt', address => 'Street 12', zipcode => '10222'}, + {cardnumber => $testGroup1Identifiers[2], branchcode => 'CPL', categorycode => 'YA', + surname => 'Pricy', firstname => 'Colt', address => 'Street 13', zipcode => '10223'}, + {cardnumber => $testGroup1Identifiers[3], branchcode => 'CPL', categorycode => 'YA', + surname => 'Expensive', firstname => 'Colt', address => 'Street 14', zipcode => '10224'}, + {cardnumber => $testGroup1Identifiers[4], branchcode => 'FPL', categorycode => 'YA', + surname => 'Cheap', firstname => 'Colt', address => 'Street 15', zipcode => '10225'}, + {cardnumber => $testGroup1Identifiers[5], branchcode => 'FPL', categorycode => 'YA', + surname => 'Poor', firstname => 'Colt', address => 'Street 16', zipcode => '10226'}, + {cardnumber => $testGroup1Identifiers[6], branchcode => 'FPL', categorycode => 'YA', + surname => 'Stingy', firstname => 'Colt', address => 'Street 17', zipcode => '10227'}, + {cardnumber => $testGroup1Identifiers[7], branchcode => 'FPL', categorycode => 'YA', + surname => 'Impoverished', firstname => 'Colt', address => 'Street 18', zipcode => '10228'}, + ); + return t::db_dependent::TestObjects::Borrowers::BorrowerFactory::createTestGroup(\@borrowers, 'cardnumber'); +} +sub deleteTestGroup1 { + t::db_dependent::TestObjects::Borrowers::BorrowerFactory::_deleteTestGroupFromIdentifiers(\@testGroup1Identifiers); +} + +1; \ No newline at end of file diff --git a/t/db_dependent/TestObjects/Issues/ExampleIssues.pm b/t/db_dependent/TestObjects/Issues/ExampleIssues.pm new file mode 100644 index 0000000..bd0fe35 --- /dev/null +++ b/t/db_dependent/TestObjects/Issues/ExampleIssues.pm @@ -0,0 +1,92 @@ +package t::db_dependent::TestObjects::Issues::ExampleIssues; + +# Copyright Vaara-kirjastot 2015 +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +use Modern::Perl; +use Carp; + +use t::db_dependent::TestObjects::Issues::IssueFactory; + +=head createTestGroupX + + You should use the appropriate Factory-class to create these test-objects. + +=cut + + +=head createTestGroup1 + + my $biblios = t::db_dependent::TestObjects::Biblios::BiblioFactory::createTestGroup1(); + my $items = t::db_dependent::TestObjects::Items::ItemFactory::createTestGroup1($biblios); + my $borrowers = t::db_dependent::TestObjects::Borrowers::BorrowerFactory::createTestGroup1(); + my $issues = t::db_dependent::TestObjects::Issues::IssueFactory::createTestGroup1(); + +You need to create the dependent test groups to use this Issues test group 1 + +=cut +sub createTestGroup1 { + my $issuesDependencies = [ { + cardnumber => '167Azel0001', + barcode => '167Nxxx0001', + daysAgoIssued => 1, + daysOverdue => -14, + },{ + cardnumber => '167Azel0002', + barcode => '167Nxxx0002', + daysAgoIssued => 1, + daysOverdue => -14, + },{ + cardnumber => '167Azel0003', + barcode => '167Nxxx0003', + daysAgoIssued => 1, + daysOverdue => -14, + },{ + cardnumber => '167Azel0004', + barcode => '167Nxxx0004', + daysAgoIssued => 1, + daysOverdue => -14, + },{ + cardnumber => '167Azel0005', + barcode => '167Nxxx0005', + daysAgoIssued => 1, + daysOverdue => -14, + },{ + cardnumber => '167Azel0006', + barcode => '167Nxxx0006', + daysAgoIssued => 1, + daysOverdue => -14, + },{ + cardnumber => '167Azel0007', + barcode => '167Nxxx0007', + daysAgoIssued => 1, + daysOverdue => -14, + },{ + cardnumber => '167Azel0008', + barcode => '167Nxxx0008', + daysAgoIssued => 1, + daysOverdue => -14, + }, + ]; + return t::db_dependent::TestObjects::Issues::IssueFactory::createTestGroup($issuesDependencies); +} +sub deleteTestGroup1 { + t::db_dependent::TestObjects::Issues::IssueFactory::_deleteTestGroupFromIdentifiers(); +} + +1; \ No newline at end of file diff --git a/t/db_dependent/TestObjects/Issues/IssueFactory.pm b/t/db_dependent/TestObjects/Issues/IssueFactory.pm new file mode 100644 index 0000000..1b225ee --- /dev/null +++ b/t/db_dependent/TestObjects/Issues/IssueFactory.pm @@ -0,0 +1,151 @@ +package t::db_dependent::TestObjects::Issues::IssueFactory; + +# Copyright Vaara-kirjastot 2015 +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +use Modern::Perl; +use Carp; + +use C4::Circulation; +use C4::Members; +use C4::Items; + +use t::db_dependent::TestObjects::Issues::ExampleIssues; +use t::db_dependent::TestObjects::ObjectFactory; + +=head t::db_dependent::TestObjects::Issues::IssueFactory::createTestGroup( $data [, $hashKey, $checkoutBranchRule] ) +Returns a Issues-HASH. +The HASH is keyed with the PRIMARY KEY, or the given $hashKey. + +@PARAM1, ARRAY of HASHes. + [ { + cardnumber => '167Azava0001', + barcode => '167Nfafa0010', + daysOverdue => 7, #This issue's duedate was 7 days ago. If undef, then uses today as the checkout day. + daysAgoIssued => 28, #This Issue hapened 28 days ago. If undef, then uses today. + }, + { + ... + } + ] +@PARAM2, String, the HASH-element to use as the returning HASHes key. +@PARAM3, String, the rule on where to check these Issues out: + 'homebranch', uses the Item's homebranch as the checkout branch + 'holdingbranch', uses the Item's holdingbranch as the checkout branch + undef, uses the current Environment branch + '', checks out all Issues from the given branchCode +=cut + +sub createTestGroup { + my ($objects, $hashKey, $checkoutBranchRule) = @_; + + #If running this test factory from unit tests or bare script, the context might not have been initialized. + unless (C4::Context->userenv()) { + C4::Context->_new_userenv('testGroupsTest'); + C4::Context->set_userenv(undef, undef, undef, + undef, undef, + 'CPL', undef, undef, + undef, undef, undef); + } + my $oldContextBranch = C4::Context->userenv()->{branch}; + + my %objects; + foreach my $issueParams (@$objects) { + my $borrower = C4::Members::GetMember(cardnumber => $issueParams->{cardnumber}); + my $item = C4::Items::GetItem(undef, $issueParams->{barcode}); + + my $duedate = DateTime->now(time_zone => C4::Context->tz()); + if ($issueParams->{daysOverdue}) { + $duedate->subtract(days => $issueParams->{daysOverdue} ); + } + + my $issuedate = DateTime->now(time_zone => C4::Context->tz()); + if ($issueParams->{daysAgoIssued}) { + $issuedate->subtract(days => $issueParams->{daysAgoIssued} ); + } + + #Set the checkout branch + my $checkoutBranch; + if (not($checkoutBranchRule)) { + #Use the existing userenv()->{branch} + } + elsif ($checkoutBranchRule eq 'homebranch') { + $checkoutBranch = $item->{homebranch}; + } + elsif ($checkoutBranchRule eq 'holdingbranch') { + $checkoutBranch = $item->{holdingbranch}; + } + elsif ($checkoutBranchRule) { + $checkoutBranch = $checkoutBranchRule; + } + C4::Context->userenv()->{branch} = $checkoutBranch if $checkoutBranch; + + my $datedue = C4::Circulation::AddIssue( $borrower, $issueParams->{barcode}, $duedate, undef, $issuedate ); + #We want the issue_id as well. + my $issues = C4::Circulation::GetIssues({ borrowernumber => $borrower->{borrowernumber}, itemnumber => $item->{itemnumber} }); + my $issue = $issues->[0]; + unless ($issue) { + carp "IssueFactory:> No issue for cardnumber '".$issueParams->{cardnumber}."' and barcode '".$issueParams->{barcode}."'"; + next(); + } + + my $key = t::db_dependent::TestObjects::ObjectFactory::getHashKey($issue, $issue->{issue_id}, $hashKey); + + $issue->{barcode} = $issueParams->{barcode}; #Save the barcode here as well for convenience. + $objects{$key} = $issue; + } + + C4::Context->userenv()->{branch} = $oldContextBranch; + return \%objects; +} + +=head + + my $objects = createTestGroup(); + ##Do funky stuff + deleteTestGroup($records); + +Removes the given test group from the DB. + +=cut + +sub deleteTestGroup { + my $objects = shift; + + my $schema = Koha::Database->new_schema(); + while( my ($key, $object) = each %$objects) { + $schema->resultset('Issue')->find($object->{issue_id})->delete(); + } +} +sub _deleteTestGroupFromIdentifiers { + my $testGroupIdentifiers = shift; + + my $schema = Koha::Database->new_schema(); + foreach my $key (@$testGroupIdentifiers) { + $schema->resultset('Issue')->find({"barcode" => $key})->delete(); + } +} + +sub createTestGroup1 { + return t::db_dependent::TestObjects::Issues::ExampleIssues::createTestGroup1(@_); +} +sub deleteTestGroup1 { + return t::db_dependent::TestObjects::Issues::ExampleIssues::deleteTestGroup1(@_); +} + +1; diff --git a/t/db_dependent/TestObjects/Items/ExampleItems.pm b/t/db_dependent/TestObjects/Items/ExampleItems.pm new file mode 100644 index 0000000..5308906 --- /dev/null +++ b/t/db_dependent/TestObjects/Items/ExampleItems.pm @@ -0,0 +1,108 @@ +package t::db_dependent::TestObjects::Items::ExampleItems; + +# Copyright Vaara-kirjastot 2015 +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +use Modern::Perl; +use Carp; + +use t::db_dependent::TestObjects::Items::ItemFactory; + +=head createTestGroupX + + You should use the appropriate Factory-class to create these test-objects. + +=cut + +my @testGroup1Identifiers = ('167Nxxx0001', '167Nxxx0002', '167Nxxx0003', '167Nxxx0004', + '167Nxxx0005', '167Nxxx0006', '167Nxxx0007', '167Nxxx0008', + ); + +sub shareItemsToBiblios { + my ($items, $biblios) = @_; + my @biblioKeys = keys %$biblios; + + for (my $i=0 ; $i<@$items ; $i++) { + my $item = $items->[$i]; + my $biblioKey = $biblioKeys[ $i % scalar(@biblioKeys) ]; #Split these Items to each of the Biblios + my $biblio = $biblios->{$biblioKey}; + + unless ($biblio && $biblio->{biblionumber}) { + carp "ExampleItems:> Item \$barcode '".$item->{barcode}."' doesn't have a biblionumber, skipping."; + next(); + } + $item->{biblionumber} = $biblio->{biblionumber}; + } +} + +=head createTestGroupX + + my $items = t::db_dependent::TestObjects::Items::ItemFactory::createTestGroup1( $biblios ); + +Creates a group of Items for you, evenly shared amongst the given Biblios. + +@PARAM1 Reference to ARRAY, of Biblio HASHes, with the biblionumber as the HASH-key. +@RETURNS Reference to ARRAY of Item-HASHes, with the 'barcode' as the HASH-key. +=cut +sub createTestGroup1 { + my $biblios = shift; + + my $items = [ + {barcode => $testGroup1Identifiers[0], + homebranch => 'CPL', holdingbranch => 'CPL', + price => '0.5', replacementprice => '0.5', itype => 'BK' + }, + {barcode => $testGroup1Identifiers[1], + homebranch => 'CPL', holdingbranch => 'FFL', + price => '1.5', replacementprice => '1.5', itype => 'BK' + }, + {barcode => $testGroup1Identifiers[2], + homebranch => 'CPL', holdingbranch => 'FFL', + price => '2.5', replacementprice => '2.5', itype => 'BK' + }, + {barcode => $testGroup1Identifiers[3], + homebranch => 'FFL', holdingbranch => 'FFL', + price => '3.5', replacementprice => '3.5', itype => 'BK' + }, + {barcode => $testGroup1Identifiers[4], + homebranch => 'FFL', holdingbranch => 'FFL', + price => '4.5', replacementprice => '4.5', itype => 'VM' + }, + {barcode => $testGroup1Identifiers[5], + homebranch => 'FFL', holdingbranch => 'FFL', + price => '5.5', replacementprice => '5.5', itype => 'VM' + }, + {barcode => $testGroup1Identifiers[6], + homebranch => 'FFL', holdingbranch => 'CPL', + price => '6.5', replacementprice => '6.5', itype => 'VM' + }, + {barcode => $testGroup1Identifiers[7], + homebranch => 'CPL', holdingbranch => 'CPL', + price => '7.5', replacementprice => '7.5', itype => 'VM' + }, + ]; + + shareItemsToBiblios($items, $biblios); + + return t::db_dependent::TestObjects::Items::ItemFactory::createTestGroup($items, 'barcode'); +} +sub deleteTestGroup1 { + t::db_dependent::TestObjects::Items::ItemFactory::_deleteTestGroupFromIdentifiers(\@testGroup1Identifiers); +} + +1; \ No newline at end of file diff --git a/t/db_dependent/TestObjects/Items/ItemFactory.pm b/t/db_dependent/TestObjects/Items/ItemFactory.pm new file mode 100644 index 0000000..7255754 --- /dev/null +++ b/t/db_dependent/TestObjects/Items/ItemFactory.pm @@ -0,0 +1,104 @@ +package t::db_dependent::TestObjects::Items::ItemFactory; + +# Copyright Vaara-kirjastot 2015 +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +use Modern::Perl; +use Carp; + +use C4::Items; + +use t::db_dependent::TestObjects::Items::ExampleItems; +use t::db_dependent::TestObjects::ObjectFactory; + +=head t::db_dependent::TestObjects::Items::ItemFactory::createTestGroup( $data [, $hashKey] ) +Returns a HASH of objects. +Each Item is expected to contain the biblionumber of the Biblio they are added into. + eg. $item->{biblionumber} = 550242; + +The HASH is keyed with the PRIMARY KEY, or the given $hashKey. + +See C4::Items::AddItem() for how the table columns need to be given. +=cut + +sub createTestGroup { + my ($objects, $hashKey) = @_; + + my %objects; + foreach my $object (@$objects) { + my ($biblionumber, $biblioitemnumber, $itemnumber); + eval { + ($biblionumber, $biblioitemnumber, $itemnumber) = C4::Items::AddItem($object, $object->{biblionumber}); + }; + if ($@) { + if (blessed($@) && $@->isa('DBIx::Class::Exception') && + $@->{msg} =~ /Duplicate entry '.+?' for key 'itembarcodeidx'/) { #DBIx should throw other types of exceptions instead of this general type :( + #This exception type is OK, we ignore this and try fetching the existing Object next. + } + else { + die $@; + } + } + my $item = C4::Items::GetItem(undef, $object->{barcode}); + unless ($item) { + carp "ItemFactory:> No item for barcode '".$object->{barcode}."'"; + next(); + } + + my $key = t::db_dependent::TestObjects::ObjectFactory::getHashKey($item, $itemnumber, $hashKey); + + $objects{$key} = $item; + } + return \%objects; +} + +=head + + my $objects = createTestGroup(); + ##Do funky stuff + deleteTestGroup($records); + +Removes the given test group from the DB. + +=cut + +sub deleteTestGroup { + my $objects = shift; + + my $schema = Koha::Database->new_schema(); + while( my ($key, $object) = each %$objects) { + $schema->resultset('Item')->find($object->{itemnumber})->delete(); + } +} +sub _deleteTestGroupFromIdentifiers { + my $testGroupIdentifiers = shift; + + my $schema = Koha::Database->new_schema(); + foreach my $key (@$testGroupIdentifiers) { + $schema->resultset('Item')->find({"barcode" => $key})->delete(); + } +} + +sub createTestGroup1 { + return t::db_dependent::TestObjects::Items::ExampleItems::createTestGroup1(@_); +} +sub deleteTestGroup1 { + return t::db_dependent::TestObjects::Items::ExampleItems::deleteTestGroup1(@_); +} + +1; \ No newline at end of file diff --git a/t/db_dependent/TestObjects/LetterTemplates/ExampleLetterTemplates.pm b/t/db_dependent/TestObjects/LetterTemplates/ExampleLetterTemplates.pm new file mode 100644 index 0000000..4e0da09 --- /dev/null +++ b/t/db_dependent/TestObjects/LetterTemplates/ExampleLetterTemplates.pm @@ -0,0 +1,92 @@ +package t::db_dependent::TestObjects::LetterTemplates::ExampleLetterTemplates; + +# Copyright Vaara-kirjastot 2015 +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +use Modern::Perl; +use Carp; + +use t::db_dependent::TestObjects::LetterTemplates::LetterTemplateFactory; + +=head createTestGroupX + + You should use the appropriate Factory-class to create these test-objects. + +=cut + +my @testGroup1Identifiers = ('circulation-ODUE1-CPL-print', 'circulation-ODUE2-CPL-print', 'circulation-ODUE3-CPL-print', + 'circulation-ODUE1-CPL-email', 'circulation-ODUE2-CPL-email', 'circulation-ODUE3-CPL-email', + 'circulation-ODUE1-CPL-sms', 'circulation-ODUE2-CPL-sms', 'circulation-ODUE3-CPL-sms', + ); + +sub createTestGroup1 { + my @letterTemplates = ( + {letter_id => $testGroup1Identifiers[0], + module => 'circulation', code => 'ODUE1', branchcode => 'CPL', name => 'Notice1', + is_html => undef, title => 'Notice1', message_transport_type => 'print', + content => 'Barcode: <>, bring it back!', + }, + {letter_id => $testGroup1Identifiers[0], + module => 'circulation', code => 'ODUE2', branchcode => 'CPL', name => 'Notice2', + is_html => undef, title => 'Notice2', message_transport_type => 'print', + content => 'Barcode: <>', + }, + {letter_id => $testGroup1Identifiers[0], + module => 'circulation', code => 'ODUE3', branchcode => 'CPL', name => 'Notice3', + is_html => undef, title => 'Notice3', message_transport_type => 'print', + content => 'Barcode: <>, bring back!', + }, + {letter_id => $testGroup1Identifiers[0], + module => 'circulation', code => 'ODUE1', branchcode => 'CPL', name => 'Notice1', + is_html => undef, title => 'Notice1', message_transport_type => 'email', + content => 'Barcode: <>, bring it back!', + }, + {letter_id => $testGroup1Identifiers[0], + module => 'circulation', code => 'ODUE2', branchcode => 'CPL', name => 'Notice2', + is_html => undef, title => 'Notice2', message_transport_type => 'email', + content => 'Barcode: <>', + }, + {letter_id => $testGroup1Identifiers[0], + module => 'circulation', code => 'ODUE3', branchcode => 'CPL', name => 'Notice3', + is_html => undef, title => 'Notice3', message_transport_type => 'email', + content => 'Barcode: <>, bring back!', + }, + {letter_id => $testGroup1Identifiers[0], + module => 'circulation', code => 'ODUE1', branchcode => 'CPL', name => 'Notice1', + is_html => undef, title => 'Notice1', message_transport_type => 'sms', + content => 'Barcode: <>, bring it back!', + }, + {letter_id => $testGroup1Identifiers[0], + module => 'circulation', code => 'ODUE2', branchcode => 'CPL', name => 'Notice2', + is_html => undef, title => 'Notice2', message_transport_type => 'sms', + content => 'Barcode: <>', + }, + {letter_id => $testGroup1Identifiers[0], + module => 'circulation', code => 'ODUE3', branchcode => 'CPL', name => 'Notice3', + is_html => undef, title => 'Notice3', message_transport_type => 'sms', + content => 'Barcode: <>, bring back!', + }, + ); + + return t::db_dependent::TestObjects::LetterTemplates::LetterTemplateFactory::createTestGroup(\@letterTemplates); +} +sub deleteTestGroup1 { + t::db_dependent::TestObjects::LetterTemplates::LetterTemplateFactory::_deleteTestGroupFromIdentifiers(\@testGroup1Identifiers); +} + +1; \ No newline at end of file diff --git a/t/db_dependent/TestObjects/LetterTemplates/LetterTemplateFactory.pm b/t/db_dependent/TestObjects/LetterTemplates/LetterTemplateFactory.pm new file mode 100644 index 0000000..0f55090 --- /dev/null +++ b/t/db_dependent/TestObjects/LetterTemplates/LetterTemplateFactory.pm @@ -0,0 +1,101 @@ +package t::db_dependent::TestObjects::LetterTemplates::LetterTemplateFactory; + +# Copyright Vaara-kirjastot 2015 +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +use Modern::Perl; +use Carp; + +use C4::Letters; + +use t::db_dependent::TestObjects::LetterTemplates::ExampleLetterTemplates; +use t::db_dependent::TestObjects::ObjectFactory qw(getHashKey); + +=head t::db_dependent::TestObjects::LetterTemplates::LetterTemplateFactory::createTestGroup( $data [, $hashKey] ) +Returns a HASH of Koha::Schema::Result::Letter +The HASH is keyed with the PRIMARY KEYS eg. 'circulation-ODUE2-CPL-print', or the given $hashKey. +=cut + +#Incredibly the Letters-module has absolutely no Create or Update-component to operate on Letter templates? +#Tests like these are brittttle. :( +sub createTestGroup { + my ($objects, $hashKey) = @_; + + my %objects; + my $schema = Koha::Database->new()->schema(); + foreach my $object (@$objects) { + my $rs = $schema->resultset('Letter'); + my $result = $rs->update_or_create({ + module => $object->{module}, + code => $object->{code}, + branchcode => ($object->{branchcode}) ? $object->{branchcode} : '', + name => $object->{name}, + is_html => $object->{is_html}, + title => $object->{title}, + message_transport_type => $object->{message_transport_type}, + content => $object->{content}, + }); + + my @pks = $result->id(); + my $key = t::db_dependent::TestObjects::ObjectFactory::getHashKey($object, join('-',@pks), $hashKey); + + $objects{$key} = $result; + } + return \%objects; +} + +=head + + my $objects = createTestGroup(); + ##Do funky stuff + deleteTestGroup($records); + +Removes the given test group from the DB. + +=cut + +sub deleteTestGroup { + my $objects = shift; + + my $schema = Koha::Database->new_schema(); + while( my ($key, $object) = each %$objects) { + $object->delete(); + } +} +sub _deleteTestGroupFromIdentifiers { + my $testGroupIdentifiers = shift; + + my $schema = Koha::Database->new_schema(); + foreach my $key (@$testGroupIdentifiers) { + my ($module, $code, $branchcode, $mtt) = split('-',$key); + $schema->resultset('Letter')->find({module => $module, + code => $code, + branchcode => $branchcode, + message_transport_type => $mtt, + })->delete(); + } +} + +sub createTestGroup1 { + return t::db_dependent::TestObjects::LetterTemplates::ExampleLetterTemplates::createTestGroup1(); +} +sub deleteTestGroup1 { + return t::db_dependent::TestObjects::LetterTemplates::ExampleLetterTemplates::deleteTestGroup1(); +} + +1; \ No newline at end of file diff --git a/t/db_dependent/TestObjects/ObjectFactory.pm b/t/db_dependent/TestObjects/ObjectFactory.pm new file mode 100644 index 0000000..1eccd42 --- /dev/null +++ b/t/db_dependent/TestObjects/ObjectFactory.pm @@ -0,0 +1,108 @@ +package t::db_dependent::TestObjects::ObjectFactory; + +# Copyright Vaara-kirjastot 2015 +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +use Modern::Perl; +use Carp; + +use t::db_dependent::TestObjects::Borrowers::BorrowerFactory; + +use Koha::Exception::BadParameter; + +=head tearDownTestContext + +Given a testContext stash populated using one of the TestObjectFactory implementations createTestGroup()-subroutines, +Removes all the persisted objects in the stash. +=cut + +sub tearDownTestContext { + my ($self, $stash) = @_; + + if ($stash->{borrowers}) { + t::db_dependent::TestObjects::Borrowers::BorrowerFactory->deleteTestGroup($stash->{borrowers}); + } +} + +sub getHashKey { + my ($self, $object, $primaryKey, $hashKey) = @_; + + if ($hashKey && not($object->{$hashKey})) { + carp ref($self)."::getHashKey($object, $primaryKey, $hashKey):> Given HASH has no \$hashKey '$hashKey'."; + } + return ($hashKey) ? $object->{$hashKey} : $primaryKey; +} + +=head validateAndPopulateDefaultValues +@INTERFACE + +Validates given Object parameters and makes sure that critical fields are given +and populates defaults for missing values. +You must overload this in the subclassing factory if you want to validate and check the given parameters +=cut + +sub validateAndPopulateDefaultValues { + my ($self, $object, $hashKey) = @_; + + unless ($object->{$hashKey}) { + Koha::Exception::BadParameter->throw(error => ref($self)."():> You want to access test Objects using hashKey '$hashKey', but you haven't supplied it as a Object parameter. ObjectFactories need a unique identifier to function properly."); + } +} + +=head _validateStashes + + _validateStashes($featureStash, $scenarioStash, $stepStash); + +Validates that the given stahses are what they are supposed to be... , HASHrefs. +@THROWS Koha::Exception::BadParameter, if validation failed. +=cut + +sub _validateStashes { + my ($self, $featureStash, $scenarioStash, $stepStash) = @_; + + if ($featureStash && not(ref($featureStash) eq 'HASH')) { + Koha::Exception::BadParameter->throw(error => __PACKAGE__."->_validateStashes():> Stash '\$featureStash' is not a HASHRef! Leave it 'undef' if you don't want to use it."); + } + if ($scenarioStash && not(ref($scenarioStash) eq 'HASH')) { + Koha::Exception::BadParameter->throw(error => __PACKAGE__."->_validateStashes():> Stash '\$scenarioStash' is not a HASHRef! Leave it 'undef' if you don't want to use it."); + } + if ($scenarioStash && not(ref($stepStash) eq 'HASH')) { + Koha::Exception::BadParameter->throw(error => __PACKAGE__."->_validateStashes():> Stash '\$stepStash' is not a HASHRef! Leave it 'undef' if you don't want to use it."); + } +} + +=head _persistToStashes + + _persistToStashes($objects, $stashKey, $featureStash, $scenarioStash, $stepStash); + +Saves the given HASH to the given stashes using the given stash key. +=cut + +sub _persistToStashes { + my ($self, $objects, $stashKey, $featureStash, $scenarioStash, $stepStash) = @_; + + if ($featureStash || $scenarioStash || $stepStash) { + while( my ($key, $borrower) = each %$objects) { + $featureStash->{$stashKey}->{ $key } = $borrower if $featureStash; + $scenarioStash->{$stashKey}->{ $key } = $borrower if $scenarioStash; + $stepStash->{$stashKey}->{ $key } = $borrower if $stepStash; + } + } +} + +1; \ No newline at end of file -- 1.9.1