the implementation of set_retval_scalar may be discouraging at first:
sub set_retval_scalar { my $self = shift;
The reason the first results were used again is to call set_retval_scalar . After the second call to set_retval_scalar , just before the second test, the internal account for Test :: MockDBI resembles
[ # first resultset { SQL => "SELECT username ...", retval => [{ username => '1234567' }, ...] }, # second resultset { SQL => "SELECT username ...", retval => [] } ]
Under the hood, when your second test query SELECT username ... , _force_retval_scalar in Test :: MockDBI looks for this data structure for the current query and stops when it first hits. Both result sets are associated with the same query, so the second has no chance of matching.
But there is hope! Please note that set_retval_scalar only copies the most external link - the link to the array you control!
Change your test a little:
my @usernames_many = ( { username => '1234567' }, { username => '2345678' }, ); my @usernames_empty = (); my $usernames = []; $mock_dbi->set_retval_scalar( MOCKDBI_WILDCARD, "SELECT username FROM location", $usernames);
With this tool, you need to modify the contents of @$usernames (i.e. the array referenced by $usernames ) in order to modify the finished query result:
@$usernames = @usernames_many; is_deeply(find_multiple_registrations($mock_db, 15), [ '1234567', '2345678' ], "many entries"); @$usernames = @usernames_empty; is_deeply(find_multiple_registrations($mock_db, 15), [ ], "no entries");
With these changes, both tests pass.
IMPORTANT: Always assign @$usernames ! You may be tempted to save a few keystrokes by writing
$usernames = [];
but this will lead to a test failure for the same reason as the test from your question: the device will continue to have the same link as in the set_retval_scalar call. Performing this method would be a wrong and misleading, unpleasant combination.
For completeness, below is a complete working example.
#! /usr/bin/perl use warnings; use strict; BEGIN { push @ARGV, "--dbitest" } use Test::MockDBI qw/ :all /; use Test::More tests => 2; my @usernames_many = ( { username => '1234567' }, { username => '2345678' }, ); my @usernames_empty = (); my $usernames = []; my $mock_dbi = get_instance Test::MockDBI; my $mock_db = DBI->connect("dbi:SQLite:dbname=:memory:", "", ""); $mock_db->{RaiseError} = 1; $mock_db->do(q{CREATE TABLE location (username char(10))}); sub find_multiple_registrations { my($dbh,$limit) = @_; my $sth = $dbh->prepare("SELECT username FROM location"); $sth->execute; [ map $_->{username} => @{ $sth->fetchall_arrayref } ]; } $mock_dbi->set_retval_scalar( MOCKDBI_WILDCARD, "SELECT username FROM location", $usernames); @$usernames = @usernames_many; is_deeply(find_multiple_registrations($mock_db, 15), [ '1234567', '2345678' ], "many entries"); @$usernames = (); is_deeply(find_multiple_registrations($mock_db, 15), [ ], "no entries");
Output:
1..2
connect () 'CONNECT TO dbi: SQLite: dbname =: memory: AS WITH'
do () 'CREATE TABLE location (username char (10))'
prepare () 'SELECT username FROM location'
execute ()
fetchall_arrayref ()
ok 1 - many entries
prepare () 'SELECT username FROM location'
execute ()
fetchall_arrayref ()
ok 2 - no entries