Использование Test :: MockDBI несколько раз с разными результатами - PullRequest
6 голосов
/ 18 августа 2010

Я пытаюсь протестировать некоторый код в разных ситуациях (для разных наборов результатов).У меня первый тест работает хорошо, но следующий пытается повторно использовать первую «таблицу».

Мои наборы результатов:

my $usernames_many = [
      { username => '1234567' },
      { username => '2345678' },
   ];
my $usernames_empty = [
   ];

но теперь, когда я пытаюсь эти вызовы:

$mock_dbi->set_retval_scalar(MOCKDBI_WILDCARD, "SELECT username FROM location", $usernames_many);
is_deeply(find_multiple_registrations($mock_db, 15), [ '1234567', '2345678' ], "many entries");

$mock_dbi->set_retval_scalar(MOCKDBI_WILDCARD, "SELECT username FROM location", $usernames_empty);
is_deeply(find_multiple_registrations($mock_db, 15), [ ], "no entries");

Первый тест проходит успешно, но второй тест дает:

not ok 3 - no entries
#   Failed test 'no entries'
#   at ./report_many_registrations_test.pl line 28.
#     Structures begin differing at:
#          $got->[0] = '1234567'
#     $expected->[0] = Does not exist

Что, по-видимому, указывает на то, что первый набор результатов использовался снова.Как я могу очистить набор результатов?Или сбросить состояние другим способом?

Ответы [ 2 ]

1 голос
/ 21 августа 2010

Реализация 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
1 голос
/ 20 августа 2010

Если вы (можете) изменить второй тест на что-то вроде:

$mock_dbi->set_retval_scalar( 
    MOCKDBI_WILDCARD, 
    "Get me username stuff",   # <= something different
    $usernames_empty
);

, то вы можете обнаружить, что тест теперь работает.

Это потому, что Test::MockDBI использует только предоставленный текст SQL и имеет заполнитель, для которого он возвращает объект DBI после сопоставления dbi->prepare( 'Get me username stuff' );

Обновление - это обходной путь, который не требуетизменение SQL:

BEGIN { push @ARGV, "--dbitest=1"; }

use 5.012;
use warnings;
use Test::More;
use Test::MockDBI ':all';

my $mock_dbi = Test::MockDBI::get_instance;
my $dbh      = DBI->connect(q{}, q{}, q{});
my $sql      = 'SELECT username FROM location';

my $Data =  [
    { username => '1234567' },
    { username => '2345678' },
];

$mock_dbi->set_retval_scalar( MOCKDBI_WILDCARD, $sql, sub { $Data } );

is_deeply( get_mock_user($dbh, $sql), [1234567,2345678], 'many entries' );

$Data = [];  # change the data!

is_deeply( get_mock_user($dbh, $sql), [], 'no entries' );

done_testing;

sub get_mock_user {
    my ($dbh, $sql) = @_;
    $dbh->prepare( $sql );
    [ map { $_->{username} } @{ $dbh->fetchrow_arrayref } ];
}

/ I3az /

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...