Upgrade to Pro — share decks privately, control downloads, hide ads and more …

perl な web application のためのテスト情報

soh335
September 20, 2013
3.6k

perl な web application のためのテスト情報

soh335

September 20, 2013
Tweet

Transcript

  1. use Test::More; use MyApp::Schema::Row::User; my $user = MyApp::Schema->user->insert({ name =>

    ...., ... => ...., ... => ...., ... => ...., ... => ...., ... => ...., }); my $something = $user->take_something; is $something, ...; done_testing; $user Λ४උ͢Δ ஋Λऔಘͯ͠ਖ਼͍͔͠ ͔֬ΊΔ
  2. use Test::More; use MyApp::Schema::Row::User; my $user = MyApp::Schema->user->insert({ name =>

    ...., ... => ...., ... => ...., ... => ...., ... => ...., ... => ...., }); ok $user->do_something; done_testing;
  3. package t::Util; use strict; use warnings; use parent 'Exporter'; our

    @EXPORT = qw(create_user); sub create_user { ... } 1;
  4. subtest test1 => sub { my $user = MyApp::Schema->user->insert({ name

    => "soh335", }); }; subtest test2 => sub { my $user = MyApp::Schema->user->insert({ name => "soh336", }); }; name ͕ uqniue ͩͱͯ͠ద ౰ʹ͚ͭΔ unique ΛอͭͨΊʹͪΐ ͬͱม͑Δ ...
  5. use Test::More; use t::Util qw(create_user) my $user = create_user(); subtest

    'foo' => sub { ok $user->do_something; }; subtest 'bar' => sub { my $something = $user->take_something; is $something, ...; }; done_testing; ̍ͭ໨ͷςετΛॻ͘ ̎ͭ໨ͷςετΛॻ͘
  6. my $user = create_user(); subtest 'foo' => sub { ok

    $user->do_something; }; subtest 'bar' => sub { my $something = $user->take_something; is $something, ...; }; subtest 'baz' => sub { ok $user->update_something; }; baz Λ௥Ճ user ͷঢ়ଶ͕ͲΜͲ ΜมΘΔ ࠷ॳͷ subtest Λमਖ਼͢Δͱ શ෦मਖ਼͢Δඞ༻͕ग़Δ͔΋͠Ε ͳ͍
  7. use Test::TimBlock; timeblock { # set_fixed_time('2013-01-01 00:00:00', '%Y-%m-%d %H:%i:%s +0900');

    set_time "2013-01-01 00:00:00"; ... }; # <- restore_time scope Λൈ͚Δͱ restore_time() ΛݺͿΑ͏ͳ wrapper Λ project ༻ʹ࡞ͬͨΓͯ͠Δ set_fixed_time( '2013-01-01 00:00:00', '%Y-%m-%d %H:%i:%s +0900' );
  8. # set_fixed_time('2013-01-01 00:00:00', '%Y-%m-%d %H:%i:%s +0900'); set_fixed_time '2013-01-01 00:00:00' =>

    sub { ... }; # <- restore_time scope Λൈ͚Δͱ restore_time() ͕ݺ͹ΕΔ Test::TimeMock::set_fixed_time ʹ sub {} Λ౉ͤΔΑ͏ʹ͢Δ
  9. • ͍͍ͩͨͷ web application ͩͬͨΒ db ͕෇͖వ͏ • temporary ͳ

    mysqld process ΛͨͯͯɺͦΕΛςετʹ࢖ ͏ • MyApp::Test ͱ͔ t::Util ͱ͔Ͱ db ͷܨ͙ઌΛ test::mysqld ʹ ্ॻ͖͢Δ
  10. package MyApp::Test; use Test::mysqld; our $mysqld; sub import { my

    $class = shift; { $mysqld = Test::mysqld->new( my_cnf => { 'skip-networking' => '', } ); MyApp->config->{ dbi } = [ $mysqld->dsn( dbname => 'test' ) ]; } } 1; ෼͔Γ΍͘͢ྫ͑Δͱ MyApp::Test ͕ use ͞ΕΔͱ dbi ͷ ઃఆΛॻ͖׵͑Δ
  11. package MyApp::Test; use Test::RedisServer; our $redis_server; sub import { my

    $class = shift; { $redis_server = Test::RedisServer->new; MyApp->config->{ redis } = $redis_server->connect_info; } } 1;
  12. use Plack::Test; test_psgi app => sub { my $env =

    shift; return [ 200, [ 'Content-Type' => 'text/plain' ], [ "Hello World" ] ], }, client => sub { my $cb = shift; my $req = HTTP::Request->new(GET => "http://localhost/hello"); my $res = $cb->($req); like $res->content, qr/Hello World/; }; from Plack::Test synopsis app ʹ request
  13. my $app = MyApp->new test_psgi( app => $app->handler, client =>

    sub { my $cb = shift; my $req = GET 'http://localhost/hello'; my $res = $cb->($req); like $res->content, qr/Hello World/; } ); ࣮ࡍʹ͸ MyApp ͱ ͔͔Β app ͱͳΔͱ͜Ζ Λ౉͢
  14. use MyApp::Test; my $req = GET 'http://localhost/hello'; my $res =

    request_to_app( $req ); like $res->content, qr/Hello World/; MyApp::Test ͱ͔Ͱָʹग़དྷΔ ͷ΋Α͍Ͱ͢Ͷ͐ HTTP::Cookie ࢖ͬͯ cookie ΋ ѻ͑ΔΑ͏ʹͨ͠Γ͢Δ
  15. my $app = MyApp->new; my $mech = Test::WWW::Mechanize::PSGI->new( app =>

    $app->handler ); $mech->get('/hello'); ok $mech->content_like(qr/Hello World/); mechanize ͷ interface Λ࢖ ͬͯςετ͕ग़དྷΔ
  16. • ఆظత or Կ͔໰୊͕͋ͬͯ batch script ͷΑ͏ͳ΋ͷ͸Α ͘ॻ͘ • ఆظతʹ࢖͏΋ͷ͸ͪΌΜͱςετ͞Ε͍ͨ

    • ໰୊͕͋ͬͯमਖ਼͢ΔΑ͏ͳ৔߹͸ͪΌΜͱಈ࡞Λอো͠ ͍ͨ • όονͷ࣮ߦΛ package ʹ֎ग़͢͠Δ
  17. use Class::Load qw(load_class); use FindBin::libs; my $name = shift @ARGV;

    my $module = "MyApp::Batch::$name"; load_class($module); $module->new_with_options->run; ίϚϯυϥΠϯ͔Β౉͞Εͨจ ࣈྻ͔Β class Λ new ͯ͠ run method ΛݺͿ perl script/batch.pl Something --go
  18. package MyApp::Batch::Something; use Mouse; with 'MouseX::Getopt'; has go => (

    is => 'ro', isa => 'Bool', default => sub { 0 } ); no Mouse; sub run { .... } 1; Mo[ou]seX::Getopt ͰίϚϯυϥΠϯͷ Ҿ਺Λ attribute ʹ͋ͯΔ
  19. package t::Util; sub trace_sql (&) { my $sub = shift;

    my @sqls; require DBIx::QueryLog; my $g = DBIx::QueryLog->guard; local $DBIx::QueryLog::OUTPUT = sub { my %params = @_; push @sqls, $params{sql}; }; $sub->(); @sqls; } 1; ͜ͷ scope ͚ͩ query Λऔ ಘ͢Δ
  20. { my @sqls = trace_sql { $user->do_heavy_something; }; is scalar

    @sqls, 10; } { my @sqls = trace_sql { $user->do_heavy_something; }; is scalar @sqls, 0; # cache ! } 10 ճ query ͕ྲྀΕ͍ͯΔ ̍ճ΋ query ͕ྲྀΕ͍ͯͳ͍
  21. is_deeply( $user, { name => "foo" relation_users => ["bar", "baz",

    "yap"], map => { 1 => "hoge", 10 => "fuga", 100 => "piyo", }, }, ); hash ͱ͔ array ͕ܾ·ͬͨύλʔϯ͡ Όͳ͘୔ࢁ͋Δͱ is_deeply ͩͱͩΔ͍࣌ ͕͋Δ
  22. cmp_deeply( $user, { name => "foo" relation_users => bag("baz", "yap",

    "bar"), map => { 1 => "hoge", 10 => "fuga", 100 => "piyo", }, }, ); Test::Deep ͔Β export ͞ΕΔ ॱং͸อোͤͣʹ಺༰͸શ෦ ͋Δ͜ͱΛςετ͢Δ ॱং͸อোͤͣʹ಺༰͸શ෦ ͋Δ͜ͱΛςετ͢Δ
  23. cmp_deeply( $user, { name => "foo" relation_users => bag("baz", "yap",

    "bar"), map => superhashof({ 1 => "hoge", 10 => "fuga", }), }, ); hash ͷҰ෦ͷ key ͚ͩςετग़ དྷΕ͹ྑ͍
  24. cmp_deeply( $user, { name => ignore(), relation_users => bag("baz", "yap",

    "bar"), map => superhashof({ 1 => "hoge", 10 => "fuga", }), }, ); hash ͷ key ͷଘࡏ͚ͩςετ ͠ɺ஋͸ԿͰ΋ྑ͍
  25. cmp_deeply( $res->content, json({ name => ignore(), relation_users => bag("baz", "yap",

    "bar"), map => superhashof({ 1 => "hoge", 10 => "fuga", }), }), ); json ʹ decode ͯ͘͠Ε Δ
  26. cmp_deeply( $res->content, json({ name => ignore(), relation_users => bag("baz", "yap",

    "bar"), map => superhashof({ 1 => is_string, 10 => "fuga", }), }), ); is_* Ͱ ignore ΑΓܕͰݫ͠໨ʹν ΣοΫग़དྷΔ
  27. cmp_deeply( $res->content, json({ name => ignore(), relation_users => is_array_ref &&

    cond { scalar @$_ == 3 }, map => superhashof({ 1 => is_string, 10 => "fuga", }), }), ); ͪΐͬͱͨ͠ࣄʹ plugin ͱ͔࡞Γ ͨ͘ͳ͍ͳʔͬͯ࣌ʹ $_ ʹ test ͞ΕΔ ஋Λ͍Εͯ͘ΕΔɻ 2 <= $_ and < 5 ͱ͔ɻ
  28. use Test::Exception; throws_ok { $user->do_something; } qr(user is died because

    ...), 'expect to die'; ͪΌΜͱྫ֎Λ౤͛ͯɺ͔ͭͦͷจ ݴΛςετ
  29. use Test::Fatal; like( exception { $user->do_something }, qr(user is died

    because ...), "expect to die", ); Test::Exception ΑΓί ʔυ͕ simple
  30. stderr_is( $user->depcateded_method(), "this method is deplicated !" ); stdout_is, stdout_like

    ͱ ͔΋͋Δ ྫ͑͹ deplicated ͳ method ͕ݺ͹ΕͨΒ warn ͢Δ͚Ͳɺ৚ ͕݅ෳࡶ͔ͩΒͪΌΜͱݺ͹ΕΔ͔ςετ ͓͖͍ͯͨ͠
  31. my $buffer = ''; open my $fh, '>', \$buffer or

    die $!; local *$STDERR = $fh; $user->depcateded_method(); is $buffer, 'this method is deplicated !'; ݸਓతʹ͸͜ΕͰࣄ଍ΓΔ ͔ͳ͊...
  32. use Test::Mock::Guard qw(mock_guard); my $guard = mock_guard( 'MyApp::Schema::Row::User', { 'take_something'

    => sub { return +{ foo => 1, bar => 2, } }, } ); is_deeply( $user->take_something, { foo => 1, bar => 2, } ); is $guard->call_count( 'MyApp::Schema::Row::User', 'take_something' ), 1; $guard ͕ scope ͷதͰଘ ࡏ͢Δ͚࣌ͩϝιουͷڍಈ Λࠩ͠ସ͑Δ ݺ͹Εͨճ਺Λς ετ
  33. • ruby ͷ webmock • https://github.com/bblimke/webmock • ֎ʹग़Α͏ͱͨ͠Β die •

    url ͚ͩ͡Όͳͯ͘ parameter, content, header ͕͋Θͳ͔ ͬͨΒ die
  34. • ઌʹηοτΞοϓࡁΈͷςετ༻ mysqld Λ༻ҙͯ͠͠·͏ • શͯͷςετ͕૸Δ࠷ॳʹ temporary ͳ mysqld Λ࡞Γɺ

    schema ΍ data Λ༻ҙ͢Δ • ֤ςετͰ db ͕ඞ༻ͳ৔߹͸͔ͦ͜Β copy Λ͠ɺmy.cnf ͳͲΛॻ͖׵͑ͯىಈ͢Δ
  35. @VTFSU @VTFSU @VTFSU @VTFSU PSJHJOBM UFTUNZTRME @VTFSU DPQZ UFTUNZTRME prove

    ։࢝࣌ʹ schema, master data ͕ೖ ͬͯΔ original test::mysqld Λ࡞Δ ֤ςετϑΝΠϧͰ ͸ original test::mysqld ͔Β copy ͠ɺmy.cnf Λमਖ਼͠ى ಈ͢Δ schema, master data Λ༻ҙ ͢ΔίετΛԼ͛ͨ
  36. @VTFSU @VTFSU @VTFSU @VTFSU UFTUNZTRME @VTFSU UFTUNZTRME UFTUNZTRME UFTUNZTRME test::mysqld

    Λฒྻ ෼ ্ཱͪ͛Δ ্ཱ͕ͪͬͯΔ mysql ͔Β࢖͑Δ΋ͷ͕ ׂΓ౰ͯΒΕΔ mysqld ͷ্ཱͪ͛ ͷίετ͕͔͔Βͳ͘ ͳΔ