Subject: | numeric comparisons against string database values |
Date: | Thu, 26 Feb 2015 12:43:07 -0500 |
To: | bug-Test-DatabaseRow [...] rt.cpan.org |
From: | John Pickard <pickardj79 [...] gmail.com> |
Test-DatabaseRow-2.04
First, thanks! Thanks for reading this email, thanks for creating and
maintaining Test::DatabaseRow.
I've run into an issue with row_ok when tests is an arrayref. The
comparison operation is evaluating to '==' when the expected looks like a
number but the database value is a string. I think this is only an issue
for '==' vs. 'eq' (not > vs. 'gt') because equality is assumed when tests
is an arrayref.
Of course this is easiest to explain with an example. For this example I
have a test database as 'testing' and have run the following sql:
create table testing.test_databaserow (
strcol varchar(20) default null
);
insert into testing.test_databaserow (strcol) values ( '11,22' );
For the following perl code, I would expect the first two tests to fail,
the third to pass. Using Test::DatabaseRow v2.04 the first test passes:
#!/usr/bin/perl
use strict;
use warnings FATAL => 'all';
use Test::DatabaseRow;
use Test::More tests => 3;
my $dbh = <YOUR DATABASE HANDLE>;
row_ok(
sql => "select strcol from testing.test_databaserow",
results => 1,
tests => [ strcol => '11' ],
dbh => $dbh,
);
row_ok(
sql => "select strcol from testing.test_databaserow",
results => 1,
tests => [ strcol => '11,' ],
dbh => $dbh,
);
row_ok(
sql => "select strcol from testing.test_databaserow",
results => 1,
tests => [ strcol => '11,22' ],
dbh => $dbh,
);
This issue may be what is referred to as an "annoying case" in the comment
on line 263 of Test::DatabaseRow::Object, however I think it is more than
annoying since it erroneously allows tests to pass. I think the problem (if
you agree it's a problem) stems from the override of the warning signal
handler on line 399, this is a bit severe for avoiding undef comparisons.
What if this was just a "no warnings 'uninitialized';"? I realize that that
doesn't really solve the issue (just produces warnings). Ignoring that,
here are my thoughts as "fixes":
1) Look at attached Object1.pm. Change $oper to "eq" if either $expect or
$got =~ /\A $RE{num}{real} \z/x on ~line 396 (really only need to do check
with $got, since we already looked at $expect when coercing tests):
my $adj_oper = $oper eq '==' && $got !~ /\A $RE{num}{real} \z/x
? 'eq'
: $oper;
Then change other uses of $oper to $adj_oper, lines 401, 406, 407, and
413-415
This feels a little bit like a hack to me but it does solve the problem.
2) Look at attached Object2.pm. Use the sig warn to set the test to failed
if a warning other than uninitialized is raised. I implemented this with a
new local variable called $got_warnings which if set will set $passed to 0.
Remove line 399 and add
my $got_warning;
no warnings 'uninitialized';
local $SIG{__WARN__} = sub { $got_warning = 1; }; # $^W not work
Then add "$passed = 0 if $got_warning;" after the eval. Unfortunately,
the diagnostics for this don't work because $oper is still '==' so _is_diag
will print the got as a number and the got/expected look the same in the
printout:
not ok 1 - simple db test
# Failed test 'simple db test'
# at ../Tickets/Testing/Packages/Test_DatabaseRow.pl line 21.
# While checking column 'strcol' on 1st row
# got: 11
# expected: 11
I do not have a solution to this diag problem :(. Maybe pass a way to
force printing as strings to_is_diag (ugh... not in Object2.pm)?
Best,
John
Message body is not shown because sender requested not to inline it.
Message body is not shown because sender requested not to inline it.