多次使用Test :: MockDBI,结果不同

时间:2010-08-18 10:50:17

标签: database perl testing

我正在尝试在不同情况下测试一些代码(针对不同的结果集)。我的第一个测试运行良好,但下一个测试是尝试重用第一个“表”。

我的结果集:

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 个答案:

答案 0 :(得分:1)

如果您(能够)将第二个测试更改为:

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

然后您可能会发现测试现在有效。

这是因为Test::MockDBI仅使用提供的SQL文本有一个占位符,在匹配dbi->prepare( 'Get me username stuff' );之后它返回DBI对象

更新 - 这是一种不需要更改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 /

答案 1 :(得分:1)

implementation of 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 ...时,Test :: MockDBI中的_force_retval_scalar会在此数据结构中搜索当前正在执行的查询,并在找到的第一个匹配时停止。两个结果集都与同一个查询相关联,因此第二个结果集没有机会匹配。

但是有希望!请注意,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);

使用此fixture,您只需要更改@$usernames的内容(即{{1>}引用的数组)来更改查询的固定结果:

$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

输出:

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