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