6 Stimmen

Mehrfache Verwendung von Test::MockDBI mit unterschiedlichen Ergebnissen

Ich versuche, einen Code in verschiedenen Situationen zu testen (für verschiedene Ergebnismengen). Der erste Test läuft gut, aber der nächste versucht, die erste "Tabelle" wiederzuverwenden.

Meine Ergebnismengen:

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

aber wenn ich jetzt diese Anrufe tätige:

$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");

Der erste Test wird bestanden, aber der zweite führt zu einem Ergebnis:

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

Dies scheint darauf hinzudeuten, dass stattdessen wieder die erste Ergebnismenge verwendet wurde. Wie kann ich eine Ergebnismenge löschen? Oder den Status auf eine andere Weise zurücksetzen?

1voto

draegtun Punkte 22205

Wenn Sie den zweiten Test in etwas wie ändern (können):

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

können Sie feststellen, dass der Test jetzt funktioniert.

Der Grund dafür ist Test::MockDBI verwendet nur den SQL-Text, der einen Platzhalter hat, für den es das DBI-Objekt nach einer passenden dbi->prepare( 'Get me username stuff' );

Update - Hier ist ein Workaround, der keine Änderung des SQL erfordert:

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/

1voto

Greg Bacon Punkte 127209

Le site Umsetzung von set_retval_scalar mag auf den ersten Blick entmutigend erscheinen:

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] };
}

Der Grund für die erneute Verwendung des ersten Ergebnissatzes sind aufeinanderfolgende Aufrufe von set_retval_scalar son kumulativ . Nach dem zweiten Aufruf von set_retval_scalar Kurz vor dem zweiten Test sieht die interne Buchhaltung von Test::MockDBI so aus

[ # first resultset
  { SQL => "SELECT username ...",
    retval => [{ username => '1234567' }, ...]
  },
  # second resultset
  { SQL => "SELECT username ...",
    retval => []
  }
]

Unter der Haube, wenn Ihre zweite Testabfrage SELECT username ... , _force_retval_scalar in Test::MockDBI durchsucht diese Datenstruktur nach der aktuell ausgeführten Abfrage und hält beim ersten gefundenen Treffer an. Beide Ergebnissätze sind mit der gleichen Abfrage verknüpft, so dass der zweite keine Chance hat, übereinzustimmen.

Aber es gibt Hoffnung! Beachten Sie, dass set_retval_scalar kopiert nur den äußersten Verweis - einen Verweis auf ein Feld, das Sie kontrollieren!

Ändern Sie Ihren Test leicht ab:

my @usernames_many = (
  { username => '1234567' },
  { username => '2345678' },
);

my @usernames_empty = ();

my $usernames = [];
$mock_dbi->set_retval_scalar(
  MOCKDBI_WILDCARD,
  "SELECT username FROM location",
  $usernames);

Mit dieser Vorrichtung müssen Sie nur den Inhalt von @$usernames (d. h. das Array bezogen auf por $usernames ), um das gespeicherte Ergebnis der Abfrage zu ändern:

@$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");

Mit diesen Änderungen werden beide Tests bestanden.

WICHTIG! Immer zuordnen @$usernames ! Sie könnten versucht sein, ein paar Tastenanschläge zu sparen, indem Sie schreiben

$usernames = [];  # empty usernames
is_deeply(find_multiple_registrations($mock_db, 15),
          [ ],
          "no entries");

aber dies wird dazu führen, dass Ihr Test aus fast dem gleichen Grund wie der Test aus Ihrer Frage fehlschlägt: Die Vorrichtung wird weiterhin den gleichen Verweis haben, den Sie ihr im Aufruf von set_retval_scalar . Eine solche Vorgehensweise wäre sowohl falsch als auch irreführend - eine unangenehme Kombination.


Der Vollständigkeit halber ist unten ein vollständiges Arbeitsbeispiel aufgeführt.

#! /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");

Ausgabe:

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X