Шпаргалка по mock-объектам в Perl

в 20:14, , рубрики: mock, perl, метки: , ,

В богатом русском языке к сожалению нет некоторых слов, поэтому вместо того чтобы начать заметку словами «Пися/Пиша модульные тесты», я вынужден удлинить фразу.
В процессе написания модульных тестов часто приходится заглядывать в старые тесты, чтобы быстро вспомнить как подменить тот или иной объект, поэтому я решил что пришло время набросать небольшую шпаргалку, авось и кому-то ещё сгодится.
Сразу прошу сильно не пинать, это шпаргалка, а не руководство, поэтому всё предельно кратко.

Подмена генератора случайных чисел

BEGIN {
  *CORE::GLOBAL::rand = sub {66};
}

Подмена времени

use Test::MockTime qw( :all );

set_fixed_time( 1305636470 );
# do something
restore_time();

CPAN: Test::MockTime

Подмена Redis

use Test::Mock::Redis;

my $redis = Test::Mock::Redis->new( server => 'whatever' );

CPAN: Test::Mock::Redis

Подмена СУБД

insert:

use DBI;
 
my $dbh     = DBI->connect( 'dbi:Mock:', '', '' );
my $history = $dbh->{mock_all_history};
 
my $st   = $history->[0];

# do that execute sql query

# check
is( $st->statement, $expected_sql, 'Query must be correct' );
is( $st->bound_params()->[0], 123456, 'Bound parameter must be correct' );

для select нужно подсунуть фальшивые результаты:

$dbh->{mock_add_resultset} = {
  sql     => 'SELECT emergency_dequeue_start()',
  results => [
    [ 'emergency_dequeue_start' ],
    [ '11' ],
   ],
};

Хотя так нужно тестить только модули работающие непосредственно с БД, в остальных случаях лучше подменять модули ORM или того что её заменяет ибо тесты эти сильно привязаны к реализации модуля, а лучше привязываться только к API.

CPAN: DBD::Mock

Подмена обращения к HTTP скрипту (LWP::UserAgent)

use Test::Mock::LWP;
 
$Mock_ua->mock( agent => sub {'My Agent 007'} );
 
$Mock_request->mock( content_type => sub {'application/x-www-form-urlencoded'} );
 
$Mock_response->mock( code        => sub {200} );
$Mock_response->mock( content     => sub {$response} );
$Mock_response->mock( is_error    => sub {0} );
$Mock_response->mock( status_line => sub {'status line'} );

Задавая значение переменной $response управляем ответом «скрипта». Если используем content() и хотим проверить верно ли сформировался запрос, то делаем так:

$Mock_ua->mock( agent => sub { 'SMSOnline plc' } );                                                                                                                                        
$Mock_request->mock( content_type => sub { 'application/x-www-form-urlencoded' } );
$Mock_request->mock( content => sub { ( my $self, $got_request ) = @_; $got_request; } );
 
$Mock_response->mock( code => sub { 200 } );
$Mock_response->mock( content => sub { $response } );
$Mock_response->mock( is_error => sub { 0 } );
$Mock_response->mock( status_line => sub { 'status line' } );

Можем проверить что в $got_request именно то что мы хотели отправить

CPAN: Test::Mock::LWP

Подмена произвольного объекта

my $mock = Test::MockObject->new();
Test::MockObject->fake_new( 'My::Module' );
my @txt_stat = (0, 0);
$mock->mock( 'read_txt', sub { return @txt_stat; } );
$mock->mock( 'write_txt', sub { 1; } );

В @txt_stat можем что-то менять для разных тестов

CPAN: Test::MockObject

Подмена Log4perl

Подмена метода debug():

my $logger = Test::MockObject->new();
Test::MockObject->fake_new( 'Log::Log4perl::init' );
 
$logger->mock( 'debug', sub { shift; diag shift; } );

Если тесты проходят нормально, то diag shift; можно убирать чтобы не засорять вывод (завалятся так можно вернуть), аналогично можно и остальные методы подmockить

Ссылки

О Test::MockObject
О DBD::Mock
Ссылки подсказал mrrico

Автор: worldmind

Поделиться

* - обязательные к заполнению поля