File Coverage

blib/lib/Test/Database/Temp.pm
Criterion Covered Total %
statement 70 76 92.1
branch 13 24 54.1
condition 6 13 46.1
subroutine 13 15 86.6
pod 2 2 100.0
total 104 130 80.0


line stmt bran cond sub pod time code
1             package Test::Database::Temp;
2             ## no critic (ControlStructures::ProhibitPostfixControls)
3              
4 1     1   351681 use strict;
  1         2  
  1         45  
5 1     1   6 use warnings;
  1         2  
  1         96  
6              
7             # ABSTRACT: Create temporary test databases and run tests in all available ones
8              
9             our $VERSION = '0.003'; # VERSION: generated by DZP::OurPkgVersion
10              
11 1     1   1000 use Module::Load qw( load );
  1         1710  
  1         9  
12 1     1   704 use English qw( -no_match_vars ); # Avoids regex performance
  1         1045  
  1         6  
13 1     1   465 use Carp;
  1         2  
  1         71  
14              
15 1     1   5 use Const::Fast;
  1         2  
  1         12  
16 1     1   793 use Log::Any;
  1         10689  
  1         9  
17 1     1   53 use Try::Tiny;
  1         3  
  1         60  
18              
19 1     1   2076 use Database::Temp ();
  1         274097  
  1         1022  
20              
21             const my @DEFAULT_DRIVERS => qw( Pg SQLite CSV );
22             const my $DEFAULT_BASENAME => 'test_database_temp_';
23              
24             sub available_drivers {
25 1     1 1 520 my ( $class, %params ) = @_;
26 1         2 my $drivers_specifically_requested;
27 1 50       4 if ( defined $params{'drivers'} ) {
28 0         0 $drivers_specifically_requested = 1;
29             }
30             else {
31 1         4 $params{'drivers'} = \@DEFAULT_DRIVERS;
32 1         3 $drivers_specifically_requested = 0;
33             }
34 1         3 my @available_drivers;
35 1         2 foreach my $driver ( @{ $params{'drivers'} } ) {
  1         5  
36 3         64 my $driver_module = "Database::Temp::Driver::${driver}";
37 3         12 load $driver_module;
38              
39 3 100       180 if ( $driver_module->is_available() ) {
40 1         97 Log::Any->get_logger->infof( 'Database::Temp driver %s available', $driver );
41 1         50 push @available_drivers, $driver;
42             }
43             else {
44 2         859 Log::Any->get_logger->infof( 'Database::Temp driver %s not available', $driver );
45             }
46             }
47 1         86 return @available_drivers;
48             }
49              
50             sub use_all_available {
51 1     1 1 332012 my ( $class, %params ) = @_;
52 1         3 my $drivers_specifically_requested;
53 1 50       7 if ( defined $params{'drivers'} ) {
54 0         0 $drivers_specifically_requested = 1;
55             }
56             else {
57 1         3 $params{'drivers'} = \@DEFAULT_DRIVERS;
58 1         2 $drivers_specifically_requested = 0;
59             }
60              
61 1         3 my $basename = $DEFAULT_BASENAME;
62 1 50       5 if ( defined $params{'basename'} ) {
63             croak "Invalid temp database basename '$params{'basename'}'"
64 0 0       0 unless $params{'basename'} =~ m/[[:alnum:]_]{1,}/msx;
65 0         0 $basename = $params{'basename'};
66             }
67              
68 1         3 foreach my $driver ( @{ $params{'drivers'} } ) {
  1         3  
69 3         3668 my $name;
70 3 50       27 if ( defined $params{'name'} ) {
71 0         0 $name = ( $basename . $params{'name'} );
72             }
73             else {
74 3         22 $name = Database::Temp::random_name();
75             }
76              
77 3         1198 my %opts;
78 3 50   0   18 $opts{init} = defined $params{'init'} ? $params{'init'} : sub { };
79 3 50   0   13 $opts{deinit} = defined $params{'deinit'} ? $params{'deinit'} : sub { };
80              
81 3         7 my $db;
82             my $e;
83             try {
84 3     3   187 my $create_params = { args => {}, };
85 3 50       23 $create_params = $params{'build'}->($driver) if ( $params{'build'} );
86             $db = Database::Temp->new(
87             driver => $driver,
88             basename => $create_params->{basename} // $basename,
89             name => $create_params->{name} // $name,
90             cleanup => $create_params->{cleanup} // 1,
91             %opts,
92             args => $create_params->{args} // {},
93 3   33     1278 );
      33        
      50        
      50        
94 1         111625 1;
95             }
96             catch {
97 2     2   40005 $e = $_;
98 3         37 };
99 3 50 66     79 if ( !$db && $drivers_specifically_requested ) {
    100          
100 0         0 croak "Could not create a temp database with requested driver '$driver'. Error: $e";
101             }
102             elsif ( !$db ) {
103 2         29 Log::Any->get_logger->infof( 'Could not create Database::Temp for driver \'%s\'. Error: ', $driver, $e );
104 2         218 next;
105             }
106 1         8 $params{'do'}->($db);
107 1 50       11772 $params{'demolish'}->($db) if $params{'demolish'};
108             }
109 1         5 return;
110             }
111              
112             1;
113              
114             __END__
115              
116             =pod
117              
118             =encoding UTF-8
119              
120             =head1 NAME
121              
122             Test::Database::Temp - Create temporary test databases and run tests in all available ones
123              
124             =head1 VERSION
125              
126             version 0.003
127              
128             =head1 SYNOPSIS
129              
130             use Test2::V0;
131             use Test::Database::Temp;
132             use DBI;
133             Test::Database::Temp->use_all_available(
134             build => sub {
135             my ($driver) = @_;
136             my %params = ( args => {} );
137             return \%params;
138             },
139             init => sub { },
140             deinit => sub { },
141             do => sub {
142             my ($db) = @_;
143             my $dbh = DBI->connect( $db->connection_info );
144             my ( $db_driver, $db_name) = ($db->driver, $db->name);
145             subtest "Testing with $db_driver in db $db_name" => sub {
146             my @row_ary;
147             my $r = try {
148             @row_ary = $dbh->selectrow_array('SELECT 1+2');
149             1;
150             } catch {
151             diag 'Failed to select 1+2';
152             };
153             is( $row_ary[0], 3, 'returned correct' );
154              
155             done_testing;
156             };
157             },
158             demolish => sub { },
159             );
160             done_testing;
161              
162             =head1 DESCRIPTION
163              
164             Test::Database::Temp is an extension to L<Database::Temp>.
165             It provides a way to easily test several different databases
166             with the same set of test.
167              
168             Test::Database::Temp has one main function: C<use_all_available>.
169             Using this subroutine user can safely test his code
170             in all available (preconfigured to test site) databases.
171             The ones which are not available are simply skipped over.
172              
173             Test::Database::Temp supports all databases available in Database::Temp.
174              
175             Test::Database::Temp uses L<Log::Any> to produce logging messages.
176              
177             =for stopwords temp database
178              
179             =head1 STATUS
180              
181             This module is currently being developed so changes in the API are possible.
182              
183             =head1 FUNCTIONS
184              
185             =head2 available_drivers
186              
187             Return a list of available Database::Temp drivers.
188              
189             If you specify the list of drivers yourself,
190             then availabity check will only be limited to those
191             and the available ones will be returned.
192             If same driver is specified several times,
193             it will be returned as many times.
194             The ordering of drivers will not changes.
195              
196             my @drivers = Test::Database::Temp->available_drivers( drivers => [ qw( SQLite SQLite Pg CSV ) ] );
197              
198             =head2 use_all_available
199              
200             Execute same code with all temporary databases.
201             This method is partly an extended version of C<Database::Temp->new()>.
202             Just like with C<Database::Temp->new()>, you can specify subroutines
203             C<init()> and C<deinit()> to initialize and teardown the databases.
204             It also has subroutines C<build> and C<demolish>.
205              
206             Use C<build()> to provide arguments to when C<Database::Temp->new()> is called
207             and C<demolish()> to clean up after database has been removed if necessary.
208              
209             You can either go with all available L<Database::Temp> drivers,
210             or you can specify a list of the wanted drivers. In the latter case,
211             if any of the wanted drivers is unavailable, subroutine will throw
212             an exception.
213              
214             One of the benefits of using B<use_all_available()> is that temporary
215             databases are created one by one, and deleted immediately after use.
216              
217             See L</SYNOPSIS> for an example.
218              
219             =head1 AUTHOR
220              
221             Mikko Koivunalho <mikkoi@cpan.org>
222              
223             =head1 COPYRIGHT AND LICENSE
224              
225             This software is copyright (c) 2023 by Mikko Johannes Koivunalho.
226              
227             This is free software; you can redistribute it and/or modify it under
228             the same terms as the Perl 5 programming language system itself.
229              
230             =cut