2016-04-22 7 views
1

Я начинаю тестировать мир в Perl. Вот мой вопрос: у меня есть класс в Perl (я использую Moo BTW), и этот класс имеет 3 атрибута (я собираюсь поставить код ниже). Одним из этих атрибутов является массив, и он создается автоматически в конструкторе. Чтобы сгенерировать массив, мне нужно получить доступ к БД и выполнить один запрос.perl Test :: MockModule with DB acces

package Customer; 
use 5.010; 
use Data::Dumper; 
use Moo; 
use TT::SQL; 

has id => (
    is=>'ro', 
    required=>1, 
); 

has type => (
    is=>'ro', 
); 

has emails => (
    is=>'rw', 
    default => sub { 
    my ($self) = @_; 
    return $self->getEmails(); 
    }, 
    lazy=> 1, 
); 

sub getEmails 
{    
       my $self=shift; 
       #obtaining the DB handler 
       my $db2 = $self->get_db_handler(); 
       my $fmuser=$self->id; 
       my $type=$self->type; 
       my $query; 
       #checking the customer type to perform the query 
       if ($type eq "tshop") 
       { 
       $query="SELECT email from XXXXXXX WHERE XXXXX=? and XXXXXXXX=1 and XXXXXXX =1'"; 
       } 
       else 
       { 
       $query="SELECT email from XXXXXXXX WHERE XXXXX=? and XXXXXXXX=1 and XXXXXXX =1"; 
       } 
       my $ref = $db2->execute($query,$fmuser); 
       my @emails; 
       #retrieving emails 
       while (my $row = $ref->fetchrow_hashref ) { 
         @emails=(@emails,"$row->{email}\n"); 
        } 
       return \@emails; 
} 

sub get_db_handler 
{ 
     my $user = "XXXXXXX"; 
    my $password = 'XXXXXXX'; 
    my $host = "XXXXX"; 
    my $driver = "Pg"; 
    my $timezone = "America/New_York"; 

    my $dsn = "DBI:$driver:database=fm;host=$host"; 
    my $db = DBI->connect($dsn,$user,$password) or die("Cannot connect to the DB !!"); 
    return $db; 

} 

1; 

Теперь я хочу запустить модульные тесты, чтобы проверить поведение предыдущего класса. До сих пор, я не буду использовать Test :: MockModule в следующем:

use diagnostics; # this gives you more debugging information 
use warnings; # this warns you of bad practices 
use strict;  # this prevents silly errors 
use Moo; 
use Data::Dumper; 
use Test::More tests => 2; # for the is() and isnt() functions 
use Customer; 
use FindBin qw/$RealBin/; 
use Test::Deep; 
use Test::MockModule; 
use DBI; 
my $dbh = DBI->connect("dbi:SQLite:dbname=$RealBin/test-cutomer.db","","") or die $DBI::errstr; 
$dbh->do(" 
CREATE TABLE IF NOT EXISTS table (
    id INTEGER PRIMARY KEY AUTOINCREMENT, 
    field1 INTEGER, 
    field2 INTEGER, 
    field3 TEXT, 
    field4 INTEGER, 
    field5 INTEGER 
) 
"); 

$dbh->do(' 
     INSERT INTO table (field1,field2,field3,field4,field5) VALUES 
     (?,?,?,?,?)',undef,undef,92,'[email protected]',1,1 
); 

END { 
    unlink "$RealBin/test-customer.db"; 
} 

{ 

my $mock = Test::MockModule->new("Customer"); 
$mock->mock("get_db_handler", sub { return $dbh }); 

my $customer=Customer->new(id=>92,type=>'other'); 

ok(defined $customer); 
my $e=$customer->emails; 
my @[email protected]$e; 
my [email protected]; 
is($length,1, 'the emails are OK'); 
} 

Я хочу издеваться метод get_db_handler для получения обработчика тест-CUSTOMER.DB и запускать запросы по этой локальной БД. До сих пор, я получаю следующее сообщение об ошибке:

1..2 
ok 1 
Can't locate object method "execute" via package "DBI::db" at Customer.pm line 
    46 (#1) 
    (F) You called a method correctly, and it correctly indicated a package 
    functioning as a class, but that package doesn't define that particular 
    method, nor does any of its base classes. See perlobj. 

Uncaught exception from user code: 
    Can't locate object method "execute" via package "DBI::db" at Customer.pm line 46. 
at Customer.pm line 46 
    Customer::getEmails('Customer=HASH(0x11359c8)') called at Customer.pm line 23 
    Customer::__ANON__('Customer=HASH(0x11359c8)') called at (eval 23) line 18 
    Customer::emails('Customer=HASH(0x11359c8)') called at t/stc.t line 66 
# Looks like you planned 2 tests but ran 1. 
# Looks like your test exited with 2 just after 1. 

Скрипт работает нормально, я имею в виду, нет никаких проблем с кодом. Проблема заключается в том, что тест . Не могли бы вы взглянуть на это? Я по достоинству оценю это. Заранее спасибо.

+0

Спасибо за быстрый ответ! Я работаю в этом примере. Но, можете ли вы объяснить немного больше о своем первом комментарии? – ccalderon911217

+0

Если мне не нужно выбирать, то и другое! hahahaha Это будет классно для меня, если вы можете предоставить мне пример использования Test: MockModule, но в этом смысле я имею в виду насмешку над объектом, который обращается к БД в конструкторе. Большое спасибо!!! – ccalderon911217

+0

Вы можете отправить сообщение на [codereview.se], если вы хотите получить полный анализ. Но пока вы хотите, чтобы клиент получал материал из какой-либо базы данных, я бы сделал свойство '$ dbh' с' has'. Таким образом, вы можете просто передать ему разные '$ dbh' в своих тестах и ​​не нужно беспокоиться насчет насмешек. Если у вас есть несколько классов, которые нуждаются в dbh, превратите их в Moo :: Role.Но обратите внимание, что sqlite может не поддерживать все функции Postgres, поэтому есть некоторые подводные камни с использованием всего подхода sqlite. Вероятно, безопаснее использовать DBIx :: Class, и пусть это беспокоится об этом. – simbabque

ответ

2

Причина вы получаете эту ошибку в том, что ваша продукция код вызова execute на базе обрабатывать, а не на заявления обрабатывать. Вы должны до prepare запрос, прежде чем вы сможете execute его.

my $sth = $db2->prepare($query); 
my $ref = $sth->execute($fmuser); 

Используя обычные имена, как $dbh, $sth и $res для ваших переменных DBI помогли бы определить, что легче.


Тест :: MockModule - это не тот инструмент, который вы используете. Это полезно, если вы хотите издеваться над зависимостями в других модулях или, возможно, только с частицами.

Но прямо сейчас у вас есть внутренняя зависимость. То, что вы хотите сделать, это зависимая инъекция, но ваш код не готов к этому, поэтому вам нужно найти другой способ.

Я бы порекомендовал использовать Sub::Override для этой работы. Это довольно просто. Он переопределяет суб в лексической области. Это все, что вам действительно нужно здесь.

use Sub::Override; 

my $sub = Sub::Override->new('frobnicate' => sub { return 'foo' }); 

Так что если вы используете это, ваш код будет выглядеть примерно так. Обратите внимание, что я очистил несколько строк в одном для второго тестового случая.

use strict; 
use warnings; 

use Test::More; 
use Sub::Override; # this line is new 

use DBI; 
use FindBin qw/$RealBin/; 

#               typo here 
my $dbh = DBI->connect("dbi:SQLite:dbname=$RealBin/test-customer.db", "", "") 
    or die $DBI::errstr; 
$dbh->do(<<'EOSQL'); 
CREATE TABLE IF NOT EXISTS table (
    id INTEGER PRIMARY KEY AUTOINCREMENT, 
    field1 INTEGER, 
    field2 INTEGER, 
    field3 TEXT, 
    field4 INTEGER, 
    field5 INTEGER 
) 
EOSQL 

$dbh->do(<<'EOSQL'); 
     INSERT INTO table (field1,field2,field3,field4,field5) VALUES 
     (?,?,?,?,?), undef, undef, 92, '[email protected]', 1, 1); 
EOSQL 

{ 
    # this makes $customer->get_db_handler temporarily return 
    # our $dbh from outside 
    my $sub = Sub::Override->new('Customer::get_db_handler' => sub { 
     return $dbh 
    }); 

    my $customer = Customer->new(id => 92, type => 'other'); 

    # this test-case is pretty useless   
    ok defined $customer, 'Customer is defined';  

    is scalar @{ $customer->emails }, 1, 
     '... and the emails were fetched form the DB'; 
} 

END { 
    unlink "$RealBin/test-customer.db"; 
} 
+0

Я получаю ту же ошибку, что и начало ... Невозможно найти метод объекта «выполнить» через пакет «DBI :: db». – ccalderon911217

+0

Не беспокойтесь! Большое спасибо! – ccalderon911217

+0

Где я могу это поместить? Я уверен, что это ошибка! Ничего себе, ты отличный! – ccalderon911217

 Смежные вопросы

  • Нет связанных вопросов^_^