Реализация set_retval_scalar
может на первый взгляд показаться обескураживающей:
sub set_retval_scalar {
my $self = shift; # my blessed self
my $type = shift; # type number from --dbitest=TYPE
my $sql = shift; # SQL pattern for badness
push @{ $scalar_retval{$type} },
{ "SQL" => $sql, "retval" => $_[0] };
}
Причина, по которой первый результирующий набор, по-видимому, снова используется, состоит в том, что последовательные вызовы set_retval_scalar
являются кумулятивный .После второго вызова set_retval_scalar
, непосредственно перед вторым тестом, внутренняя бухгалтерия для Test :: MockDBI напоминает
[ # first resultset
{ SQL => "SELECT username ...",
retval => [{ username => '1234567' }, ...]
},
# second resultset
{ SQL => "SELECT username ...",
retval => []
}
]
Под капотом, когда ваш второй тест запрашивает SELECT username ...
, _force_retval_scalar
в тесте:: MockDBI ищет в этой структуре данных текущий выполняемый запрос и останавливается на первом найденном совпадении.Оба набора результатов связаны с одним и тем же запросом, поэтому у второго нет шансов на совпадение.
Но есть надежда!Обратите внимание, что set_retval_scalar
копирует только самую внешнюю ссылку - ссылку на массив, которым вы управляете!
Немного измените ваш тест:
my @usernames_many = (
{ username => '1234567' },
{ username => '2345678' },
);
my @usernames_empty = ();
my $usernames = [];
$mock_dbi->set_retval_scalar(
MOCKDBI_WILDCARD,
"SELECT username FROM location",
$usernames);
С помощью этого прибора,вам нужно только изменить содержимое @$usernames
(то есть массив , на который ссылается на $usernames
), чтобы изменить стандартный результат запроса:
@$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");
С этими изменениямиоба теста пройдены.
ВАЖНО: Всегда назначать на @$usernames
!Вы можете испытать желание сохранить несколько нажатий клавиш, написав
$usernames = []; # empty usernames
is_deeply(find_multiple_registrations($mock_db, 15),
[ ],
"no entries");
, но это приведет к сбою теста по той же причине, что и тест из вашего вопроса: прибор будет по-прежнему иметь ту же ссылку, что иВы дали это в вызове set_retval_scalar
.Делать это таким образом было бы неправильно и вводить в заблуждение, неприятная комбинация.
Для полноты, полный рабочий пример приведен ниже.
#! /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");
Вывод:
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