Slide 1

Slide 1 text

Тестирование 1 / 55

Slide 2

Slide 2 text

2 / 55

Slide 3

Slide 3 text

Степень изолированности Модульное тестирование Интеграционное тестирование Системное тестирование 3 / 55

Slide 4

Slide 4 text

Goals of Test Automation T ests should help us improve quality. T ests should help us understand the SUT . T ests should reduce (and not introduce) risk. T ests should be easy to run. T ests should be easy to write and maintain. T ests should require minimal maintenance as the system evolves around them. 4 / 55

Slide 5

Slide 5 text

Are tests QA? 5 / 55

Slide 6

Slide 6 text

Test the interface, not the implementation 6 / 55

Slide 7

Slide 7 text

t/* $ ls t factory.t pod.t 7 / 55

Slide 8

Slide 8 text

TAP $ perl t/factory.t 1..15 ok 1 - get_fields ok 2 - build ok 3 - create ... ok 10 - related_factory helper ok 11 - related_factory_batch helper ok 12 - create with excluded param ok 13 - after_get_fields ok 14 - after_build ok 15 - after_create 8 / 55

Slide 9

Slide 9 text

TAP $ perl t/factory.t 1..15 ok 1 - get_fields ok 2 - build ok 3 - create ... ok 10 - related_factory helper ok 11 - related_factory_batch helper ok 12 - create with excluded param ok 13 - after_get_fields ok 14 - after_build not ok 15 - after_create # Failed test 'after_create' # at t/factory.t line 290. # Compared $data->[0]->sum # got : '123' # expect : '1123' # Looks like you failed 1 test of 15. 9 / 55

Slide 10

Slide 10 text

prove $ prove t/factory.t .. ok t/pod.t ...... ok All tests successful. Files=2, Tests=17, 0 wallclock secs (...) Result: PASS 10 / 55

Slide 11

Slide 11 text

prove $ prove t/factory.t .. 1/15 # Failed test 'after_create' # at t/factory.t line 290. # Compared $data->[0]->sum # got : '123' # expect : '1123' # Looks like you failed 1 test of 15. t/factory.t .. Dubious, test returned 1 ... Failed 1/15 subtests t/pod.t ...... ok Test Summary Report ------------------- t/factory.t (Wstat: 256 Tests: 15 Failed: 1) Failed test: 15 Non-zero exit status: 1 Files=2, Tests=17, 0 wallclock secs (...) Result: FAIL 11 / 55

Slide 12

Slide 12 text

TAP::Harness use TAP::Harness; my $h = TAP::Harness->new(\%args); $h->runtests("A", "B", "C"); 12 / 55

Slide 13

Slide 13 text

Test::Builder use Test::Builder; my $test = Test::Builder->new; $test->ok(1 == 1, 'one'); $test->is_eq 2, 7, 'two'; $test->done_testing(); ok 1 - one not ok 2 - two # Failed test 'two' # at T.pm line 6. # got: '2' # expected: '7' 1..2 # Looks like you failed 1 test of 2. 13 / 55

Slide 14

Slide 14 text

Test::Simple use Test::Simple tests => 42; ok(sin(0), 0, 'Sin(0)'); 14 / 55

Slide 15

Slide 15 text

Test::More use Test::More tests => 42; use Test::More; # ... done_testing($n); 15 / 55

Slide 16

Slide 16 text

ok, is, isnt ok(sin(0) == 0, '...'); is(sin(0), 0, '...'); isnt($result, 'error', '...'); 16 / 55

Slide 17

Slide 17 text

like, unlike like($error, qr/forbidden/, '...'); unlike($error, qr/forbidden/, '...'); 17 / 55

Slide 18

Slide 18 text

cmp_ok cmp_ok($x, '==', $y); cmp_ok($x, '&&', $y); 18 / 55

Slide 19

Slide 19 text

can_ok can_ok("Dog", qw(bark run)); can_ok($dog, qw(bark run)); foreach my $method (qw(bark run)) { can_ok($dog, $method, "method $method"); } 19 / 55

Slide 20

Slide 20 text

isa_ok, new_ok my $obj = Some::Module->new; isa_ok( $obj, 'Some::Module' ); new_ok("Dog" => ['Pluto', 42]); 20 / 55

Slide 21

Slide 21 text

subtest subtests sinus => sub { is(Sin(0), 0, 'zero'); is(Sin(PI/2), 1, 'pi/2'); }; 1..1 # Subtest: sinus ok 1 - zero ok 2 - pi/2 1..2 ok 1 - sinus 21 / 55

Slide 22

Slide 22 text

pass, fail my $name = '...'; pass($name); fail($name); 22 / 55

Slide 23

Slide 23 text

require_ok, use_ok require_ok 'My::Module'; require_ok 'My/Module.pm'; BEGIN { use_ok('Some::Module', qw(foo bar)) } 23 / 55

Slide 24

Slide 24 text

is_deeply is_deeply( {1 => [1,2,3]}, {1 => [1,2,3]}, '...' ); 24 / 55

Slide 25

Slide 25 text

diag, note pass('A'); pass('B'); diag('DIAG'); note('NOTE'); pass('C'); pass('D'); 1..4 ok 1 - A ok 2 - B # DIAG # NOTE ok 3 - C ok 4 - D T.pm .. 1/4 # DIAG T.pm .. ok All tests successful. Files=1, Tests=4, 0 wallclock secs (...) Result: PASS 25 / 55

Slide 26

Slide 26 text

explain sub explain { my $self = shift; return map { ref $_ ? do { $self->_try( sub { require Data::Dumper }, die_on_fail => 1 ); my $dumper = Data::Dumper->new( [$_] ); $dumper->Indent(1)->Terse(1); $dumper->Sortkeys(1) if $dumper->can("Sortkeys"); $dumper->Dump; } : $_ } @_; } 26 / 55

Slide 27

Slide 27 text

SKIP use Test::More tests => 4; SKIP: { skip('because we are learning', 4) if 1; fail('A'); fail('B'); pass('C'); pass('D'); } 1..4 ok 1 # skip because we are learning ok 2 # skip because we are learning ok 3 # skip because we are learning ok 4 # skip because we are learning 27 / 55

Slide 28

Slide 28 text

TODO TODO: { local $TODO = 'we are learning'; fail('A'); fail('B'); pass('C'); pass('D'); } 1..4 not ok 1 - A # TODO we are learning # Failed (TODO) test 'A' # at T.pm line 6. not ok 2 - B # TODO we are learning # Failed (TODO) test 'B' # at T.pm line 7. ok 3 - C # TODO we are learning ok 4 - D # TODO we are learning 28 / 55

Slide 29

Slide 29 text

todo_skip TODO: { local $TODO = 'we are learning'; todo_skip('Learning!', 4); fail('A'); fail('B'); pass('C'); pass('D'); } not ok 1 # TODO & SKIP Learning! not ok 2 # TODO & SKIP Learning! not ok 3 # TODO & SKIP Learning! not ok 4 # TODO & SKIP Learning! 29 / 55

Slide 30

Slide 30 text

BAIL_OUT require_ok($module) or BAIL_OUT("Can't load $module"); 30 / 55

Slide 31

Slide 31 text

Test::Class package My::Cube::Test; use base qw(Test::Class); use Test::More; use My::Cube; sub test_volume : Test(2) { my ($self) = @_; my $cube = My::Cube->new(x => 2); is($cube->volume, 8, 'regular cube'); $cube->x(0); is($cube->volume, 0, 'trivial cube'); return; } sub test_diagonal : Test(4) { ... } 31 / 55

Slide 32

Slide 32 text

setup, teardown package My::Cube::Test; use base qw(Test::Class); use Test::More; use My::Cube; sub init_cube : Test(setup) { my ($self) = @_; $self->{cube} = My::Cube->new(x => 2); } sub test_volume : Test(2) { my ($self) = @_; is($self->{cube}->volume, 8, 'regular cube'); $self->{cube}->x(0); is($self->{cube}->volume, 0, 'trivial cube'); return; } 32 / 55

Slide 33

Slide 33 text

startup, shutdown use My::Test; use base qw(Test::Class); sub db_connect : Test(startup) { shift->{dbi} = DBI->connect(...); } sub db_disconnect : Test(shutdown) { shift->{dbi}->disconnect; } package My::Some::Module::Test; use base qw(My::Test); 33 / 55

Slide 34

Slide 34 text

Test::Class::Load use Foo::Test; use Foo::Bar::Test; use Foo::Fribble::Test; use Foo::Ni::Test; Test::Class->runtests; use Test::Class::Load qw(t/tests t/lib); Test::Class->runtests; package My::Test::Class; use base 'Test::Class'; INIT { Test::Class->runtests() } 1; 34 / 55

Slide 35

Slide 35 text

TEST DESCRIPTIONS sub one_plus_one_is_two : Test { is(1+1, 2); } ok 1 - one plus one is two 35 / 55

Slide 36

Slide 36 text

TODO sub live_test : Test { local $TODO = "live currently unimplemented"; ok(Object->live, "object live"); } 36 / 55

Slide 37

Slide 37 text

Наследование package My::Test; use base qw(Test::Class); package My::Some::Module::Test; use base qw(My::Test); sub SKIP_CLASS { 1 } package My::Some::Module::A::Test; use base (My::Some::Module::Test); package My::Some::Module::B::Test; use base (My::Some::Module::Test); 37 / 55

Slide 38

Slide 38 text

Организация use Local::OK::Post; use Local::OK::Post::Test; lib/Local/OK/Post.pm t/lib/Local/OK/Post/Test.pm t/class.t 38 / 55

Slide 39

Slide 39 text

Test::Class::Moose package TestsFor::DateTime; use Test::Class::Moose; use DateTime; sub test_constructor { my $test = shift; $test->test_report->plan(3); can_ok 'DateTime', 'new'; my %args = (year => 1967, month => 6, day => 20); isa_ok my $date = DateTime->new(%args), 'DateTime'; is $date->year, $args{year}, '... and the year should be correct'; } 1; 39 / 55

Slide 40

Slide 40 text

Test::Deep my $name_re = re('^(Mr|Mrs|Miss) \w+ \w+$'); cmp_deeply( $person, { Name => $name_re, Phone => re(q{^0d{6}$}), ChildNames => array_each($name_re) }, "person ok" ); 40 / 55

Slide 41

Slide 41 text

cmp_deeply cmp_deeply( [{1 => 2}, {3 => 4}], [{1 => 2}, {3 => 4}], ); 41 / 55

Slide 42

Slide 42 text

ignore() cmp_deeply( [{1 => 2}, {3 => 4}], [{1 => 2}, {3 => ignore()}], ); 42 / 55

Slide 43

Slide 43 text

methods cmp_deeply( $obj, methods( name => "John", ["favourite", "food"] => "taco" ) ); cmp_deeply( $obj, listmethods( name => "John", ["favourites", "food"] => ["Mapo tofu", "Gongbao chicken"] ) ); 43 / 55

Slide 44

Slide 44 text

re cmp_deeply($got, [ re("ferg") ]) 44 / 55

Slide 45

Slide 45 text

bag cmp_deeply([1, 2, 2], bag(2, 2, 1)) 45 / 55

Slide 46

Slide 46 text

all, any cmp_deeply( $got, all(isa("Person"), methods(name => 'John')) ); any( re("^John"), all(isa("Person"), methods(name => 'John')) ); re("^John") | isa("Person") & methods(name => 'John') 46 / 55

Slide 47

Slide 47 text

array_each my $common_tests = all( isa("MyFile"), methods( handle => isa("IO::Handle") filename => re("^/home/ted/tmp"), ) ); cmp_deeply($got, array_each($common_tests)); 47 / 55

Slide 48

Slide 48 text

Fixtures use Test::DBIx::Class; $schema->resultset($source_name)->populate([...]); 48 / 55

Slide 49

Slide 49 text

...::PopulateMore {Gender => { fields => 'label', data => { male => 'male', female => 'female', }}}, {Person => { fields => ['name', 'age', 'gender'], data => { john => ['john', 38, "!Index:Gender.male"], jane => ['jane', 40, '!Index:Gender.female'], }}}, 49 / 55

Slide 50

Slide 50 text

...::PopulateMore {FriendList => { fields => [ 'person', 'friend', 'created_date' ], data => { john_jane => [ '!Index:Person.john', '!Index:Person.jane' '!Date: March 30, 1996', ], }}}, 50 / 55

Slide 51

Slide 51 text

DBIx::Class::Factory :-) package My::UserFactory; use base qw(DBIx::Class::Factory); __PACKAGE__->resultset( My::Schema->resultset('User') ); __PACKAGE__->fields({ name => __PACKAGE__->seq( sub {'User #' . shift} ), status => 'new', }); package My::SuperUserFactory; use base qw(DBIx::Class::Factory); __PACKAGE__->base_factory('My::UserFactory'); __PACKAGE__->field(superuser => 1); 51 / 55

Slide 52

Slide 52 text

DBIx::Class::Factory :-] my $user = My::UserFactory->create(); my @verified_users = @{ My::UserFactory->create_batch( 3, {status => 'verified'} ) }; my $superuser = My::SuperUserFactory->build(); $superuser->insert(); 52 / 55

Slide 53

Slide 53 text

Test Double Dummy object T ets Stub T est Spy Mock Object 53 / 55

Slide 54

Slide 54 text

Test::MockModule; use Module::Name; use Test::MockModule; { my $module = Test::MockModule- new('Module::Name'); $module->mock('subroutine', sub { ... }); Module::Name::subroutine(@args); # mocked } Module::Name::subroutine(@args); # orig 54 / 55

Slide 55

Slide 55 text

Test::MockObject; use Test::MockObject; my $mock = Test::MockObject->new(); $mock->set_true('somemethod'); ok($mock->somemethod()); $mock->set_true('foo') ->set_false('bar') ->set_series('baz', 'a', 'b', 'c'); ); 55 / 55

Slide 56

Slide 56 text

No content