$30 off During Our Annual Pro Sale. View Details »

新時代のテストフレームワークTest2

akiym
July 01, 2017

 新時代のテストフレームワークTest2

YAPC::Fukuoka 2017 HAKATA

akiym

July 01, 2017
Tweet

More Decks by akiym

Other Decks in Technology

Transcript

  1. ৽࣌୅ͷ
    ςετϑϨʔϜϫʔΫ
    5FTU
    BLJZN
    :"1$'VLVPLB)","5"

    View Slide

  2. BLJZN

    View Slide

  3. ΞδΣϯμ
    w 1FSMͰςετΛॻ͘
    w 5FTUͷ঺հ
    w 5FTUҠߦͷ஫ҙ఺
    w 5FTUͰςετΛॻ͘
    w ςετศར৘ใ

    View Slide

  4. 1FSMͰ
    ςετΛॻ͘

    View Slide

  5. 5FTU.PSF
    JT MJLF JT@EFFQMZ TVCUFTU
    EPOF@UFTUJOH

    View Slide

  6. w JT஋ͷνΣοΫ
    w MJLFਖ਼نදݱύλʔϯʹϚον͢Δ͔
    w JT@EFFQMZσʔλߏ଄ಉ࢜ͷൺֱ
    w 4,*1
    50%0

    View Slide

  7. w ಺෦Ͱ͸5FTU#VJMEFSΛར༻
    sub ok ($;$) {
    my( $test, $name ) = @_;
    my $tb = Test::More->builder;
    return $tb->ok( $test, $name );
    }
    5FTU.PSFQN

    View Slide

  8. 5FTU%FFQ
    σΟʔϓͳൺֱ

    View Slide

  9. w σʔλߏ଄TFU CBH TVQFSIBTIPG
    w ΦϒδΣΫτͷϝιουͷνΣοΫ
    w ֦ுੑ͕ߴ͍5FTU%FFQ
    use Test::Deep;
    cmp_deeply [[1,3,2], {a => 1, b => 2, c => 3}],
    [set(1,2,3), superhashof({a => 1})];
    ॱෆಉ LFZBͷνΣοΫɺ΄͔͸ແࢹ

    View Slide

  10. 5FTU'BUBM
    ྫ֎

    View Slide

  11. w ྫ֎͕ൃੜ͍ͨͯ͠͠ͳ͍νΣοΫ
    w ஥ؒ5FTU&YDFQUJPO
    use Test::Fatal;
    like exception { die "fatal" }, qr/fatal/;
    lives_ok { 'alive' };

    View Slide

  12. 5FTU.PDL(VBSE
    ͦͷείʔϓͷΈͰ
    ༗ޮͳϞοΫΛ࡞Δ

    View Slide

  13. use Test::Mock::Guard;
    my $g = mock_guard 'ExternalAPI', {
    fetch => sub { 1 },
    };
    {
    my $g = mock_guard 'ExternalAPI', {
    fetch => sub { 0 },
    };
    ExternalAPI->fetch; #=> 0
    }
    ExternalAPI->fetch; #=> 1
    ͜ͷείʔϓ಺ͷΈͰ༗ޮ

    View Slide

  14. 5FTU8BSOJOHT
    ܯࠂ͕ग़͍ͯͳ͍͔
    νΣοΫ

    View Slide

  15. w ಛఆͷείʔϓͰXBSOJOHΛัଊ
    w ҙਤ͠ͳ͍XBSOJOH͕ग़ͯͳ͍͔
    use Test::Warnings;
    like warning { warn 'test' }, qr/test/;
    warn 'debug';
    done_testing; # fail!

    View Slide

  16. 5FTU$MBTT
    Y6OJUͬΆ͘ॻ͚Δ

    View Slide

  17. w TFUVQUFBSEPXO TUBSUVQTIVUEPXO
    w ؀ڥม਺5&45@.&5)0%Λࢦఆͯ͠
    ಛఆͷςετͷΈ࣮ߦ

    View Slide

  18. package Foo;
    use parent qw/Test::Class/;
    sub setup : Test(setup) {
    $_[0]->{something} = 'init';
    }
    sub foo : Tests { ... }
    sub bar : Tests { ... }
    __PACKAGE__->runtests;
    5&45@.&5)0%GPPQSPWFUFTUU

    View Slide

  19. ςετ͸ศརͰ

    ͋Ε͹͋Δ΄Ͳخ͍͠

    View Slide

  20. ςετϑϨʔϜϫʔΫࣗମΛ
    ֦ுͨ͘͠ͳͬͯ͘Δ

    View Slide

  21. UUFTUQNʹ
    ॻ͔ΕͨศརϋοΫ

    View Slide

  22. ϞϯΩʔύον

    View Slide

  23. 5FTU#VJMEFSͷ֦ு͸େม

    View Slide

  24. 5FTUͷ

    ঺հ

    View Slide

  25. View Slide

  26. 5FTU#VJMEFSΛஔ͖׵͑Δ
    ৽͍͠ςετϑϨʔϜϫʔΫ

    View Slide

  27. w 5FTU#VJMEFSΛϦϑΝΫλϦϯά
    w 5FTU4JNQMFEJTUSJCVUJPO

    όʔδϣϯҎ߱
    w QFSM͔Β͸͕

    ඪ४Ϟδϡʔϧͱͯ͠ೖ͍ͬͯΔ

    View Slide

  28. ৽͍͠ͱ͸
    w ςετͷςετ͕͠΍͍͢
    w ֦ு͠΍͍͢ɺίʔυ͕੔ཧ͞Ε͍ͯΔ
    w Ҏલ͸5FTU#VJMEFSʹϞϯΩʔύον
    ͢Δ͔͠ͳ͔ͬͨ
    w ςετࣦഊ࣌ͷϨϙʔτ͕Θ͔Γ΍͍͢

    View Slide

  29. 5FTU#VJMEFSࣗମ͕
    5FTU"1*Ͱ

    ࣮૷͠௚͞Ε͍ͯΔ

    View Slide

  30. 5FTUҠߦͷ
    ஫ҙ఺

    View Slide

  31. جຊతʹ͸Ҏલͷ
    5FTU#VJMEFSͱͷޓ׵ੑ͕
    ҡ࣋͞Ε͍ͯΔ

    View Slide

  32. طଘͷςετ͕յΕΔ͔ʁ
    ˠେৎ෉

    View Slide

  33. ͨͩ͠5FTU#VJMEFSʹ
    ϞϯΩʔύον
    Λ͍ͯ͠ΔͱյΕΔ

    View Slide

  34. ؾ͍ͮͨΒ5FTUʹ

    ͳ͍ͬͯͨΒ

    View Slide

  35. $BSUPOͰόʔδϣϯ

    ݻఆ͍ͯ͠ͳ͍
    ґଘϞδϡʔϧ͕5FTUʹґଘ

    View Slide

  36. View Slide

  37. ٸʹςετ͕յΕΔલʹ
    5FTUҠߦ͠·͠ΐ͏

    View Slide

  38. 5FTUҠߦͷ஫ҙ఺Λ঺հ

    View Slide

  39. ࣄྫ

    View Slide

  40. VUGIBDLΛ΍ΊΔ

    View Slide

  41. use utf8;
    use Test::More;
    pass 'ਖ਼͍͠';
    done_testing;
    Wide character in print at /.../Test2/Formatter/
    TAP.pm line 113.
    ok 1 - ਖ਼͍͠
    1..1

    View Slide


  42. 5FTU#VJMEFSʹϞϯΩʔύονͯ͠

    ग़ྗ࣌ʹVUGFODPEF

    View Slide

  43. binmode Test::More->builder->$_, ":utf8" for
    qw/output failure_output todo_output/;
    no warnings 'redefine';
    my $code = \&Test::Builder::child;
    *Test::Builder::child = sub {
    my $builder = $code->(@_);
    binmode $builder->$_, ":utf8" for qw/output failure_output todo_output/;
    return $builder;
    };
    *__ORIGINAL::Test::Builder::_print_to_fh = \&Test::Builder::_print_to_fh;
    *Test::Builder::_print_to_fh = sub {
    my ($self, $fh, @args) = @_;
    binmode $fh, ':utf8';
    goto \&__ORIGINAL::Test::Builder::_print_to_fh;
    }

    View Slide

  44. 5FTUͳΒ
    VTF5FTU1MVHJO65'
    ͢Δ͚ͩ

    View Slide

  45. use utf8;
    use Test::More;
    use Test2::Plugin::UTF8;
    pass 'ਖ਼͍͠';
    done_testing;
    ࣮ࡍ͸UUFTUQN಺ʹॻ͍͓͍ͯͯ
    ςετϑΝΠϧຖʹVTFUUFTU͢Δͱ
    ศར

    View Slide

  46. ࣄྫ

    View Slide

  47. &/%είʔϓ಺Ͱςετ͠ͳ͍

    View Slide

  48. 5FTU$MBTTͷSVOUFTUTΛ
    ࣗಈͰݺͿϋοΫ

    View Slide

  49. package t::test;
    sub import {
    my $pkg = caller;
    eval qq{
    package $pkg;
    use parent qw/Test::Class/;
    use Test::More;
    END {
    $pkg->runtests;
    }
    };
    }
    package Foo;
    use t::test;
    sub foo : Tests {
    fail;
    }
    # __PACKAGE__->runtests; # ෆཁ
    ऴྃ࣌ʹSVOUFTUT͕ݺ͹ΕΔ
    ςετ

    View Slide

  50. not ok 1 - foo
    # Failed test 'foo'
    # at /.../Test/Builder.pm line 135.
    # (in main->foo)
    1..1
    # Looks like you failed 1 test of 1.
    τϨʔε͕յΕΔ
    ςετग़ྗ

    View Slide

  51. ͳͥʁ

    View Slide

  52. 5FTU"1*QN
    &/%ͷͱ͖͸ৗʹDBMMFS
    ʹͳΔ

    View Slide

  53. 5FTU"1*QN

    View Slide

  54. w ͦ΋ͦ΋&/%ͷڍಈ͸೉͍͠
    w &/%Λར༻ͯ͠ςετΛݺΜͰ͍Δ
    έʔε͸ผͷํ๏Λར༻͢Δ

    View Slide

  55. package t::test;
    use Hook::AfterRuntime ();
    sub import {
    my $pkg = caller;
    eval qq{
    package $pkg;
    use parent qw/Test::Class/;
    use Test::More;
    };
    Hook::AfterRuntime::after_runtime {
    $pkg->runtests;
    };
    }

    View Slide

  56. package Foo;
    use strict;
    use warnings;
    use t::test;
    sub foo : Tests {
    }
    package Foo;
    use t::test;
    use warnings;
    use strict;
    my $__ENDRUNAAAAAAAA =
    'Hook::AfterRuntime'->new(0);
    sub foo {
    &pass();
    }
    package Foo;
    use attributes ('Foo', sub {
    package Foo;
    use warnings;
    use strict;
    &pass();
    }
    , 'Tests');
    %FQBSTF
    σετϥΫλ͕ݺ͹ΕΔ ϑΝΠϧ
    ͷऴΘΓ
    ίʔυΛJNQPSUઌʹ
    ૠೖ͍ͯ͠Δ

    View Slide

  57. ϞϯΩʔύον͍ͯ͠Δ
    ςετϞδϡʔϧ΋յΕΔ

    View Slide

  58. w 5FTU1SFUUZ
    w 5FTU8BSOJOHT

    View Slide

  59. 5FTUʹΑΔϝϦοτ΋͋Δ

    View Slide

  60. 5FTUͰςετ
    Λॻ͘

    View Slide

  61. 5FTU4VJUF

    View Slide

  62. 5FTUΛϕʔεʹ

    ศརͳςετؔ਺Λఏڙ

    View Slide

  63. VTF5FTU7

    View Slide

  64. DPNQBSF
    σʔλߏ଄ͷൺֱ
    5FTU%FFQ

    View Slide

  65. JT
    JT@EFFQMZ
    DNQ@EFFQMZ
    JT
    MJLF

    View Slide

  66. my $ref = {a => 1, b => 2, c => [1, 2, 3]}
    # σʔλߏ଄͕ਖ਼͍͔͠νΣοΫ
    is $ref, {a => 1, b => 2, c = [1, 2, 3]};
    # ࢦఆͨ͠΋ͷҎ֎͸νΣοΫ͠ͳ͍
    like $ref, {b => 2};

    View Slide

  67. ςετ͕GBJMͨ͠ͱ͖ͷग़ྗ

    View Slide

  68. use Test2::V0;
    my $ref = {a => ['A'], b => 'baz'};
    like $ref, hash {
    field a => array { item 'a' };
    field b => match qr/bar/;
    # ଘࡏ͢Δ͔
    field c => E();
    end();
    };

    View Slide

  69. not ok 1
    # Failed test at test.t line 9.
    # +--------+-----------------------+---------+--------------+------+
    # | PATH | GOT | OP | CHECK | LNs |
    # +--------+-----------------------+---------+--------------+------+
    # | | HASH(0x7fbddc028400) | | | 3, 9 |
    # | {a} | ARRAY(0x7fbddc028418) | | | 4 |
    # | {a}[0] | A | eq | a | 4 |
    # | {b} | baz | =~ | (?^:bar) | 5 |
    # | {c} | | !exists | | 7 |
    # +--------+-----------------------+---------+--------------+------+
    ςετग़ྗ

    View Slide

  70. NPDL
    ϞοΫ
    5FTU.PDL(VBSE

    View Slide

  71. my $mock = mock 'Foo' => (
    add => [add_method => sub { ... }],
    override => [replace_method => sub { ... }],
    );
    Foo->add_method;
    Foo->replaced_method;

    View Slide

  72. w BEE PWFSSJEF
    w CFGPSF BGUFS BSPVOE
    w $MBTT.FUIPE.PEJpFSTΈ͍ͨͳͷ
    w 5FTU.PDL(VBSEͰ͸೉͔ͬͨ͠

    View Slide

  73. FYDFQUJPO
    ྫ֎ͷνΣοΫ
    5FTU'BUBM

    5FTU&YDFQUJPO

    View Slide

  74. like(dies { die 'xxx' }, qr/xxx/);
    ok(lives { ... });

    View Slide

  75. XBSOJOHT
    ܯࠂͷνΣοΫ
    5FTU8BSO
    5FTU8BSOJOHT

    View Slide

  76. like warning { warn 'xxx' }, qr/xxx/;
    ok no_warnings { ... };

    View Slide

  77. ཚ਺ͷTFFEʹ஫ҙ
    w σϑΥϧτͩͱຊ೔ͷ೔෇

    4FFEFETSBOEXJUITFFEGSPNMPDBMEBUF
    w ؀ڥม਺5@3"/%@4&&%ͰࢦఆՄೳ
    JTB@PLͷڍಈʹ΋஫ҙ

    View Slide

  78. my $ref = {};
    ref_ok($ref, 'HASH', 'ϋογϡϦϑΝϨϯε͔Ͳ͏͔');
    isa_ok([], 'ARRAY');
    # Failed test 'ARRAY->isa('ARRAY')'
    # at test.t line 3.
    # ARRAY is neither a blessed reference or a
    package name.
    5FTU4VJUFͷJTB@PLͷڍಈ͸5FTU.PSFͱҟͳΔ

    View Slide

  79. ςετ
    ศར৘ใ

    View Slide

  80. ςετΛͭͷؔ਺ʹ
    ·ͱΊΔ

    View Slide

  81. use Test::More;
    sub my_ok {
    ok($_[0]);
    }
    my_ok(0);
    my_ok(0);
    not ok 1
    # Failed test at test.pl line 4.
    not ok 2
    # Failed test at test.pl line 4. Ͳͷݺͼग़͠ݩͰGBJM͔ͨ͠Θ͔Βͳ͘ͳΔ

    View Slide

  82. ղܾํ๏
    ্ͭͷDBMMTUBDLΛݟΔΑ͏
    ʹ఻͑Δ

    View Slide

  83. use Test::More;
    sub my_ok {
    local $Test::Builder::Level =
    $Test::Builder::Level + 1;
    ok($_[0]);
    }
    5FTU#VJMEFS
    MPDBM5FTU#VJMEFS-FWFM
    ͱॻ͍ͯ͸͍͚ͳ͍

    View Slide

  84. use Test::More;
    use Test2::API qw/context/;
    sub my_ok {
    my $ctx = context();
    ok($_[0]);
    $ctx->release;
    }
    5FTU

    View Slide

  85. not ok 1
    # Failed test at test.pl line 10.
    not ok 2
    # Failed test at test.pl line 11.

    View Slide

  86. 5FTU1MVHJO4PVSDF%JBH

    View Slide

  87. ςετͷग़ྗ݁Ռʹ
    GBJMͨ͠ߦͷιʔείʔυΛ
    දࣔ

    View Slide

  88. use Test2::V0;
    use Test2::Plugin::SourceDiag;
    my $something = 'test';
    my $pat = qr/test2/;
    like $something, $pat;
    done_testing;

    View Slide

  89. not ok 1 - is $something, $pat;
    # Failed test 'is $something, $pat;'
    # at test.t line 6.
    # +------+----+------------+
    # | GOT | OP | CHECK |
    # +------+----+------------+
    # | test | =~ | (?^:test2) |
    # +------+----+------------+
    ςετग़ྗ

    View Slide

  90. 5FTU%FFQͷΑ͏ʹॻ͖͍ͨ

    View Slide

  91. 5FTU%FFQ-JLF
    HJUIVCDPNBLJZN5FTU%FFQ-JLF

    View Slide

  92. use Test::Deep;
    cmp_deeply $foo, isa('Foo') &
    methods(foo => 'FOO');
    5FTU%FFQ
    5FTU4VJUF
    use Test2::V0;
    is $foo, object {
    prop blessed => 'Foo';
    call foo => 'FOO';
    };

    View Slide

  93. use Test2::V0;
    use Test2::DeepLike;
    is $foo, isa('Foo') & methods(foo => 'FOO');
    5FTU%FFQ-JLF

    View Slide

  94. not ok 1
    # Failed test at t/methods.t line 16.
    # Compared $data->foo
    # got : 'BAR'
    # expect : 'FOO'
    5FTU%FFQ
    5FTU%FFQ-JLF
    not ok 1
    # Failed test at t/methods.t line 16.
    # +-------+-----+----+-------+
    # | PATH | GOT | OP | CHECK |
    # +-------+-----+----+-------+
    # | foo() | BAR | eq | FOO |
    # +-------+-----+----+-------+

    View Slide

  95. use Test2::V0;
    use Test2::DeepLike;
    my $foo = bless {}, 'Foo';
    my $bar = bless {}, 'Bar';
    is [$foo, $bar], bag(isa('Foo'), isa('Baz'));

    View Slide

  96. not ok 1
    # Failed test at test.t line 7.
    # Comparing $data as a Bag
    # Missing: 1 reference
    # Extra: 1 reference
    5FTU%FFQ
    5FTU%FFQ-JLF
    not ok 1
    # Failed test at test.t line 7.
    # +------+--------------------------+---------+------------------+-----+
    # | PATH | GOT | OP | CHECK | LNs |
    # +------+--------------------------+---------+------------------+-----+
    # | [*] | | ISA | Baz | 7 |
    # | [1] | Bar=HASH(0x7f91c0803268) | !exists | | |
    # +------+--------------------------+---------+------------------+-----+

    View Slide

  97. ·ͱΊ

    View Slide

  98. w ςετϞδϡʔϧͷ঺հ
    w 5FTUҠߦͷ஫ҙ఺
    w VTF5FTU1MVHJO65'
    w 5FTUͷςετͷॻ͖ํ
    w 5FTUͰςετΛศརʹ͢Δ

    View Slide