$30 off During Our Annual Pro Sale. View Details »
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
MR::Go::Admin::Test
Search
Vadim Pushtaev
January 23, 2017
Programming
0
87
MR::Go::Admin::Test
Vadim Pushtaev
January 23, 2017
Tweet
Share
More Decks by Vadim Pushtaev
See All by Vadim Pushtaev
PyCon Russia 2018 — Unit Testing
vadimpushtaev
0
410
Perl course (5), testing
vadimpushtaev
0
110
Perl course (4), testing
vadimpushtaev
0
110
Perl course (4), db
vadimpushtaev
0
220
Perl course (4), oop
vadimpushtaev
1
190
Perl course (4), mod
vadimpushtaev
0
200
Perl course (3), testing
vadimpushtaev
0
98
Perl course (3), db
vadimpushtaev
0
85
Perl course (3), oop
vadimpushtaev
0
110
Other Decks in Programming
See All in Programming
Deno Tunnel を使ってみた話
kamekyame
0
210
TestingOsaka6_Ozono
o3
0
170
AIエージェントの設計で注意するべきポイント6選
har1101
5
1.9k
Graviton と Nitro と私
maroon1st
0
130
AI 駆動開発ライフサイクル(AI-DLC):ソフトウェアエンジニアリングの再構築 / AI-DLC Introduction
kanamasa
11
3.3k
マスタデータ問題、マイクロサービスでどう解くか
kts
0
110
AtCoder Conference 2025「LLM時代のAHC」
imjk
2
560
AIエージェントを活かすPM術 AI駆動開発の現場から
gyuta
0
450
AIコーディングエージェント(Manus)
kondai24
0
210
エディターってAIで操作できるんだぜ
kis9a
0
750
The Art of Re-Architecture - Droidcon India 2025
siddroid
0
120
Github Copilotのチャット履歴ビューワーを作りました~WPF、dotnet10もあるよ~ #clrh111
katsuyuzu
0
120
Featured
See All Featured
Thoughts on Productivity
jonyablonski
73
5k
How To Stay Up To Date on Web Technology
chriscoyier
791
250k
Taking LLMs out of the black box: A practical guide to human-in-the-loop distillation
inesmontani
PRO
3
1.9k
AI Search: Where Are We & What Can We Do About It?
aleyda
0
6.7k
BBQ
matthewcrist
89
9.9k
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
2
120
[RailsConf 2023] Rails as a piece of cake
palkan
58
6.2k
Documentation Writing (for coders)
carmenintech
77
5.2k
Ten Tips & Tricks for a 🌱 transition
stuffmc
0
31
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
359
30k
Kristin Tynski - Automating Marketing Tasks With AI
techseoconnect
PRO
0
110
The browser strikes back
jonoalderson
0
70
Transcript
MR::Go::Admin::Test
Go::Admin Go
Go::Admin Go Go Go Go Go Go Go Go Go
Go
xUnit
xUnit SUnit
xUnit SUnit JUnit
xUnit SUnit JUnit unittest
xUnit SUnit JUnit unittest runit
xUnit SUnit JUnit unittest runit rspec
xUnit SUnit JUnit unittest runit rspec Test::Class
xUnit Test Patterns Автор: Месарош Джерард
package MR::Go::Admin::User::Test; sub setup : Test(setup) { my ($self) =
@_; $self->{user} = create_user(); } sub test_delete : Test(2) { my ($self) = @_; $self->{user}->delete(); # ... } Test::Class
Именование классов MR::Go::Admin::User → MR::Go::Admin::User::Test
Именование классов MR::Go::Admin::User → MR::Go::Admin::User::Test *::Test запрещены не в тестах
Именование классов MR::Go::Admin::User → MR::Go::Admin::User::Test *::Test запрещены не в тестах
MR::Go::Admin::User::Test::_FakeUser
Именование методов delete → delete delete → delete__already_deleted _delete →
delete delete → test_delete delete → test_delete__already_deleted _delete → test__delete__already_deleted Двойное подчеркивание
https://goo.gl/tBIRwZ sub _set_status { my ($self, $status) = @_; $self->_status($status);
$self->_history->add($status); $self->_log('...'); } sub set_status_processing { return shift->_set_status('processing'); } sub set_status_processed { return shift->_set_status('processing'); } Тестирование приватных методов
Структура теста
Структура теста Arrange Act Assert
Структура теста Arrange Act Assert Setup Exercise Verify Teardown
База
База Поддельная база
База Поддельная база SQLite
База Поддельная база SQLite Настоящая база
База Поддельная база SQLite Настоящая база Test::DBIx::Class
Fixtures
Fixtures Копия реальной базы
Fixtures Копия реальной базы Слепок реальной базы
Fixtures Копия реальной базы Слепок реальной базы Искусственно подготовленный слепок
Fixtures Копия реальной базы Слепок реальной базы Искусственно подготовленный слепок
Пустая база
Fixtures Копия реальной базы Слепок реальной базы Искусственно подготовленный слепок
Пустая база Фабрики
package MR::Go::Admin::UserFactory; use base qw(DBIx::Class::Factory); __PACKAGE__->resultset(MR::Go::Admin:: Schema->resultset('User')); __PACKAGE__->fields({ name =>
__PACKAGE__->seq(sub {'User #' . shift}), city => __PACKAGE__->related_factory('MR::Go::Admin::CityFactory'), status => 'new', }); DBIx::Class::Factory
package MR::Go::Admin::UserFactory; use base qw(DBIx::Class::Factory); __PACKAGE__->resultset(MR::Go::Admin:: Schema->resultset('User')); __PACKAGE__->fields({ name =>
__PACKAGE__->seq(sub {'User #' . shift}), city => __PACKAGE__->related_factory('MR::Go::Admin::CityFactory'), status => 'new', }); https://goo.gl/tBIRwZ DBIx::Class::Factory
Фабрика в деле sub set_status : Test(1) { my ($self)
= @_; my $task = MR::Admin::Go::Test::Factory::Task->create(); $task->set_status('TEST_NEW_STATUS'); is($task->status, 'TEST_NEW_STATUS', 'status'); return; }
Транзакции
Транзакции setup = BEGIN
Транзакции setup = BEGIN teardown = ROLLBACK
Транзакции setup = BEGIN teardown = ROLLBACK $self->break_db();
Test doubles
Test doubles Mock
Test doubles Mock Spy
Test doubles Mock Spy Stub
Test doubles Mock Spy Stub Dummy
Моки с магией my $mock = Test::MockModule->new('MR::Go::Admin::SMS'); my $sent =
0; $mock->mock(_send => sub { $sent = 1 });
Моки с магией local *MR::Go::Admin::SMS::_send = sub { $sent++ };
my $mock = Test::MockModule->new('MR::Go::Admin::SMS'); my $sent = 0; $mock->mock(_send => sub { $sent = 1 });
Моки без магии package MR::Go::Admin::User::Test::_FakeUser; MR::Go::Admin::User::Test::_FakeUser->new(); bless $user, 'MR::Go::Admin::User::Test::_FakeUser';
Моки без магии has _logger => (default => sub {
'...' }); User->new({_logger => $fake_logger}); User->new({_message_size_limit => 42}); package MR::Go::Admin::User::Test::_FakeUser; MR::Go::Admin::User::Test::_FakeUser->new(); bless $user, 'MR::Go::Admin::User::Test::_FakeUser';
Asserts is($node->children->count, 0, 'leaf node');
Asserts is($node->children->count, 0, 'leaf node'); is($rating, 42); cmp_ok($status, 'eq', 'processed');
ok($user->deleted);
is($node->children->count, 0, 'leaf node'); is($rating, 42); cmp_ok($status, 'eq', 'processed'); ok($user->deleted);
Asserts use Test::Deep;
cmp_deeply( $users, bag($user1, $user2) ); cmp_deeply( $user, methods( rating =>
42, blocked => bool(0), ) ); Test::Deep
Спасибо Вадим Пуштаев
[email protected]
https://goo.gl/SNrhWp Дизайн Максим Гелета
Бонус: ExportCatcher { package MR:Go::Admin::Test::_ExportCatcher; use MR::Go::Admin::Schema; use Test::DBIx::Class; }