SlideShare a Scribd company logo
あんなテスト・こんなテスト        (This and That about testing)




                             土田 拓也(Takuya Tsuchida)
                                                             @tsucchi
  2011/10/15
                                                      id: tsucchi1022

 エレクトロニクス事業本部


Public/公開情報     -1-
Abstract

  テストの話をします
     (I'll be talking about testing)
  とくに「テストしにくい部分をどのようにテストするか」について話し
   ます
     (Especially, I'll talk about how to test the part which is hard to test)




Public/公開情報                                -2-
About Me

  土田 拓也(Takuya Tsuchida)
  所属: 凸版印刷株式会社
   エレクトロニクス事業本部 システム開発部
     (TOPPAN PRINTING Co., LTD
     Electronics Division System Development team)
  仕事: MES(製造実行システム)の開発・運用など
               (Develop and operate MES(Manufacturing Execution System))
              – DB 設計したり、SQL 書いたり、Perl 書いたりしています
                  (designing DB schema, writing SQL and Perl etc)
    CPAN(PAUSE): TSUCCHI
    id(hatena): tsucchi1022
    twitter: @tsucchi
    github: https://github.com/tsucchi



Public/公開情報                                      -3-
testcodes

  テストコード、書いてますか?
     (Do You Write testcodes?)
  テストコードとは(What is the testcode?)
              – 「入力」と「その入力に対して、期待する出力」を書いて、一致するかど
                   うかを検証するプログラム
                (programs which validates 'input' and 'expected output from the
                   provided input' are correct.)




Public/公開情報                               -4-
Automated Testing (2)

  Example) testing add() subroutine
        #!/usr/bin/perl -w
        use strict;
        use warnings;

        use Test::More;
                             Subroutine to be tested
        sub add {
            my (@inputs) = @_;
            my $result = 0;
            for my $input ( @inputs ) {
                $result += $input;
            }
            return $result;
        }
                         Input for test
        # testing 'add' subroutine         Expected output
        is( add(1, 2),        3 );
        is( add( (1 .. 10) ), 55 );

        done_testing();


Public/公開情報                                    -5-
Strong points and Weak Points

  長所(strong points)
     – 繰り返し実行できる(It enables to run any time)
               • 改修やリファクタリングでエンバグしていないか容易
                     に調べられる(You can easily find whether enbug or not when you
                    finished bug-fix or refactorings)
                • Jenkins などの CI サーバと組み合わせることで、コ
                   ミット時などの任意のタイミングでテストを実施でき
                   る(Combine with CI server, It is enable to run tests any time such as
                    after commit)
  短所(weak points)
     – イニシャルコストが上がる (increase initial costs)
     – テストを書きにくい場合がある (Sometimes, It is hard to write testcodes)




Public/公開情報                                 -6-
When It is hard to write testcode

  「入力」や「出力」が明確ではなかったり、作りにくい場合
     (Inputs or outputs are ambiguous or hard to make these)
     – 標準入出力(STDIN/STDOUT)
     – コマンドラインオプション (commandline options)
     – 時刻(system clock)
     – DB
     – etc.
  これらを、「なんとかする」やり方を紹介します
     (I'll introduce how to deal with such things)




Public/公開情報                                          -7-
   標準入出力(STDIN/STDOUT)
    コマンドラインオプション (commandline options)
    時刻(system clock)
    DB
    etc.




Public/公開情報                  -8-
Tests for STDIN/STDOUT

    Principle
         – 内部のロジックが良くテストされているなら、無理して実施する必
               要は無い(If internal logic is well tested, It is no need to test STDIN/STDOUT
                forcefully)

    Example Situation
        – コマンドラインツールのテストをしたい(want to test command-line tool)
        – 外部モジュールの中間出力が見たいが、その内容が標準出力
             を使っている(middle output for external modules, but the output is printed in
                STDOUT/STDERR)
         – warn/carp の内容を確認したい(want to check output by warn/carp)
    Solution
         – use IO::Scalar
         – use Capture::Tiny(for STDOUT/STDERR)
         – use IO::Capture::STDOUT/STDERR
         – tie STDIN/STDOUT/STDERR
Public/公開情報
Automated Testing (2)

  Ex 1)using IO::Scalar and capture STDIN
        #!/usr/bin/perl -w
        use strict;
        use warnings;
        use Test::More;

        sub add {
            my $result = 0;
            while( <STDIN> ) {
                chomp;
                $result += $_;
            }
            return $result;
        }

        subtest 'add', sub {
            my $inputs = "1n2n3n"; # input from STDIN
            open my $stdin_fh, '<', $inputs;
            local *STDIN = *$stdin_fh; # replace default STDIN
            is( add(), 6 );
        };
        done_testing();

Public/公開情報                                - 10 -
Automated Testing (2)

  Ex 2) using Capture::Tiny(for STDOUT/STDERR)
        #!/usr/bin/perl -w
        use strict;
        use warnings;
        use Test::More;
        use Capture::Tiny qw(capture);

        sub add {
            my($a, $b) = @_;
            print $a + $b;
        }

        my ($stdout) = capture {
            add(1, 2);
        };
        is($stdout, 3);
        done_testing();

      – 簡単に使えるので、Test::Warn の代用とするのも良いと思う
         (I think it's good idea to use this module alternate for Test::Warn)


Public/公開情報                              - 11 -
   標準入出力(STDIN/STDOUT)
    コマンドラインオプション (commandline options)
    時刻(system clock)
    DB
    etc.




Public/公開情報                  - 12 -
Tests for command-line args

    Principle
              – 内部のロジックが良くテストされているなら、無理して実施する必
                 要は無い(If internal logic is well tested, It is no need to test
                  STDIN/STDOUT forcefully)
    Example Situation
              – コマンドラインツールのテストをしたい(want to test command-line
                  tool)
              – GetOpt::* を使わず、自前でオプション解析しているのを直したい
                  (want to fix because it has self-implemented command-line
                  args analysis)
    Solution
              – @ARGV を書き換える




Public/公開情報
Tests for command-line args

  Ex) testing command-line args
     #!/usr/bin/perl -w
     use strict;
     use warnings;
     use Test::More;
     our $a_str = undef;
     # 本当は別 package にある / in real case, this subroutine is defined in other package
     sub read_args {
         while ( $_ = shift @ARGV ) {
             if ( $_ =~ /^-a$/ ) {
                 $a_str = shift @ARGV;
             }
             # ... other option analyses are follows
         }
     }
     subtest 'a option with arg test', sub {
         $a_str = undef;
         local @ARGV = ("-a", "a_value"); #ここにオプションを指定 / passes options here
         read_args();
         is($a_str, "a_value");
     };
     done_testing();

Public/公開情報                              - 14 -
   標準入出力(STDIN/STDOUT)
    コマンドラインオプション (commandline options)
    時刻(system clock)
    DB
    etc.




Public/公開情報                  - 15 -
Tests for system clock

    Principle
              – 時刻に依存せずテストが書かれるべき(Tests should be written in no
                  depencency to system clock)
    Example Situation
              – ログの日付フォーマットが正しいかチェックしたい(want to test
                  datetime format in logs)
              – ロット番号など、日付によって処理内容が変わるものをテストした
                 い(want to test what changes depending on datetime such as lot-no)
    Solution
              – 時刻を改竄する(alter perl's system clock)
                  • use Test::MockTime
                  • use Time::Mock
                  • CORE::GLOBAL::time() を書き換える(override
                           CORE::GLOBAL::time)



Public/公開情報
Tests for system clock

  Ex) Test::MockTime
        #!/usr/bin/perl
        use strict;
        use warnings;

        BEGIN { $ENV{TZ} = 'JST' }

        use Test::MockTime qw(set_fixed_time);
        use Test::More;
        use POSIX qw(strftime);

        sub some_lot_no { return strftime("%Y%m%d-%H%M%S", localtime()); }

        set_fixed_time('2009-03-23T11:22:33');
        is( some_lot_no(), '20090323-112233');
        done_testing();




Public/公開情報                                - 17 -
   標準入出力(STDIN/STDOUT)
    コマンドラインオプション (commandline options)
    時刻(system clock)
    DB
    etc.




Public/公開情報                  - 18 -
Tests for DB

    Principle
              – 基本はモック(DBD::Mock)を使うべき(Mock should be used)
              – ビジネスロジックと DB は切り離すべき(Business logics and DB should be
                 separated)
    Example Situation
              – ストアドプロシージャをテストしたい(want to test stored procedure)
              – ORM を使わず、生の DBI を使っているので SQL をテストしたい
                 (want to test SQL because we don't use ORM)
    Solution
              – データを流し込む(load data into DB)
              – Test::mysqld + something
                      • Test::Fixture::DBI
                      • Test::DBUnit
                    • Test::DataLoader::MySQL



Public/公開情報
   標準入出力(STDIN/STDOUT)
    コマンドラインオプション (commandline options)
    時刻(system clock)
    DB
    etc.




Public/公開情報                  - 20 -
exit measures(1)

  テスト中に exit が呼ばれると、意図せずテストが通ってしまう
     (If exit() is called, tests are passed accidentally)

  Example Situation
              – 他人のコードを引き継いだ際(when takeover someone's code)
              – ライブラリがエラー処理後に exit を呼んでいた(library routine calls
                   exit after error handling)
  Solution
       – exit()の上書き(override exit)
       – exit を使っている関数/メソッドの上書き(override
                   subroutine/method which uses exit)




Public/公開情報                                          - 21 -
Exit measures(2)

  Ex1) exit() causes problem
        #!/usr/bin/perl -w
        use strict;
        use warnings;
        use Test::More 'no_plan';
        my $important_value = '';
        sub evil_operation { $important_value = "aaa"; exit 0; }

        ok(1);
        evil_operation();
        is( $important_value, '' );



        % prove exit.t
        exit.t .. ok
        All tests successful.
        Files=1, Tests=1, 0 wallclock secs ( 0.04 usr   0.00 sys +
        0.01 cusr 0.01 csys = 0.06 CPU)
        Result: PASS


     – This test successes unexpectedly
Public/公開情報                                - 22 -
Exit measures(3)

  Ex2) measured exit() call
     #!/usr/bin/perl -w
     use strict;
     use warnings;
     use Test::More 'no_plan';
     BEGIN { *CORE::GLOBAL::exit = sub { die 'unexpected exit called!' } } # ADD THIS!
     my $important_value = '';

     sub evil_operation { $important_value = "aaa"; exit 0; }

     ok(1);
     evil_operation();
     is( $important_value, '' );


     % prove exit_measured.t
     exit_measured.t .. 1/? unexpected exit called! at
     exit_measured.t line 6.
     ...(snip)
     Result: FAIL

     – It's OK. It should be failed.
Public/公開情報                                - 23 -
setUp/tearDown(1)

  xUnit を使っていた人は setUp/tearDown が使いたいかも
     (xUnit users may want to use setup/tearDown)

              – それ Test::Class で出来るよ!
               (Test::Class enables it!)

      #!/usr/bin/perl -w
      use strict;
      use warnings;
      use Test::Class;

      MyTest->runtests();

      package MyTest;
      use parent qw(Test::Class);
      use Test::More;

      sub set_up    :Test(setup)    {      diag("setup"); }
      sub tear_down :Test(teardown) {      diag("teardown"); }

      sub my_test :Test(1) {        ok(1);        #this is some test }
      sub my_test2 :Test(1) {       is("1", "1"); #this is another test }

Public/公開情報                                     - 24 -
setUp/tearDown(2)

  でも書き方が変わるのは面倒くさい
     (But it isn't good that how to write test is changed)

              – subtest + Hook::LexWrap
      #!/usr/bin/perl -w
      use strict;
      use warnings;
      use Test::More;
      use Hook::LexWrap;

      wrap 'subtest',
          pre => sub { diag("setup");    }, #alternate for setup
          post => sub { diag("teardown");} #alternate for teardown
      ;

      subtest 'my_test', sub { ok(1); };
      subtest 'my_test2', sub { is("1", "1"); };

      done_testing();




Public/公開情報                                        - 25 -
setUp/tearDown(3)

  実行例(execution example)
      % perl setup_teardown.t
      # setup                   setup called
           ok 1
           1..1
      ok 1 - my_test                  test called
      # teardown
      # setup                       teardown called
           ok 1
           1..1
      ok 2 - my_test2
      # teardown
      1..2




Public/公開情報                                    - 26 -
Caution(1)

  今回紹介したテクニックをプロダクションコード側で使わない
     (Don't use these techniques in production code)

              – プロダクションコードでモンキーパッチしたり、時間や
                 CORE::GLOBAL::* を書き換えたり、@ARGV 書き換えた
                 り、標準入出力捕まえたりしないこと
               (In production code, don't monkey-patch, don't alter system clock, don't replace
                   CORE::GLOBAL::*, don't replace @ARGV, and don't capture STDIN/STDOUT)
              – テストでは有効なテクニックでも、プロダクションコードで使
                 うと妙なバグに振り回されるかもしれません
               (These techniques are useful in testcode, but you may encounter curious bugs if
                  using it in production code)




Public/公開情報                                      - 27 -
Caution(2)


  「テストしにくい部分を何とかしたい!」という考えは基本的には何
     かが間違っています
     (I think it is wrong opinion such as 'I want to manage testcode which is hard to be written')
              – 段階的に直していくべき(It should be fixed gradually)
              – Mock 使うとか、他の部分をテストしてカバーするとか
               (using Mock or tests other parts to cover it)




Public/公開情報                                        - 28 -
Conclusion

  テストしにくいものも、Perl だと結構なんとかなります
     (Sometimes it is hard to write testcode, but Perl provides power to make it possible)

  「どうやったら、このテストしにくいコードを何とかできるか」を考え
     るのは結構楽しい
     (It is fun thinking about how to write testcode which is hard to be written)

              – とくにレガシーコードを相手にする場合は
               (especially for legacy codes)



  テストを書きましょう!辛いテストでも Perl なら何とかな
    ります!
     (Let's write testcode! Perl enables you to provide power to write hard tests)




Public/公開情報                                        - 29 -
Public/公開情報   - 30 -
質疑をうけて、ちょっとだけ補足(発表後に追記)
     exit で意図せずテストが成功する場合の話ですが、これは
       「no_plan」にしているときのみ発生する事象です。

     比較的新しい Test::More を使っていれば、done_testing()
      が使えるので、それを使うべきです。また、5.8.8 とかに
      標準添付される Test::More だと done_testing()が使え
      ないバージョンなので、その場合は no_plan をそもそも
      避けるべきです。

     (sorry only in Japanese)




Public/公開情報                     - 31 -

More Related Content

あんなテスト、こんなテスト(this and that about testing)

  • 1. あんなテスト・こんなテスト (This and That about testing) 土田 拓也(Takuya Tsuchida) @tsucchi 2011/10/15 id: tsucchi1022 エレクトロニクス事業本部 Public/公開情報 -1-
  • 2. Abstract  テストの話をします (I'll be talking about testing)  とくに「テストしにくい部分をどのようにテストするか」について話し ます (Especially, I'll talk about how to test the part which is hard to test) Public/公開情報 -2-
  • 3. About Me  土田 拓也(Takuya Tsuchida)  所属: 凸版印刷株式会社 エレクトロニクス事業本部 システム開発部 (TOPPAN PRINTING Co., LTD Electronics Division System Development team)  仕事: MES(製造実行システム)の開発・運用など (Develop and operate MES(Manufacturing Execution System)) – DB 設計したり、SQL 書いたり、Perl 書いたりしています (designing DB schema, writing SQL and Perl etc)  CPAN(PAUSE): TSUCCHI  id(hatena): tsucchi1022  twitter: @tsucchi  github: https://github.com/tsucchi Public/公開情報 -3-
  • 4. testcodes  テストコード、書いてますか? (Do You Write testcodes?)  テストコードとは(What is the testcode?) – 「入力」と「その入力に対して、期待する出力」を書いて、一致するかど うかを検証するプログラム (programs which validates 'input' and 'expected output from the provided input' are correct.) Public/公開情報 -4-
  • 5. Automated Testing (2)  Example) testing add() subroutine #!/usr/bin/perl -w use strict; use warnings; use Test::More; Subroutine to be tested sub add { my (@inputs) = @_; my $result = 0; for my $input ( @inputs ) { $result += $input; } return $result; } Input for test # testing 'add' subroutine Expected output is( add(1, 2), 3 ); is( add( (1 .. 10) ), 55 ); done_testing(); Public/公開情報 -5-
  • 6. Strong points and Weak Points  長所(strong points) – 繰り返し実行できる(It enables to run any time) • 改修やリファクタリングでエンバグしていないか容易 に調べられる(You can easily find whether enbug or not when you finished bug-fix or refactorings) • Jenkins などの CI サーバと組み合わせることで、コ ミット時などの任意のタイミングでテストを実施でき る(Combine with CI server, It is enable to run tests any time such as after commit)  短所(weak points) – イニシャルコストが上がる (increase initial costs) – テストを書きにくい場合がある (Sometimes, It is hard to write testcodes) Public/公開情報 -6-
  • 7. When It is hard to write testcode  「入力」や「出力」が明確ではなかったり、作りにくい場合 (Inputs or outputs are ambiguous or hard to make these) – 標準入出力(STDIN/STDOUT) – コマンドラインオプション (commandline options) – 時刻(system clock) – DB – etc.  これらを、「なんとかする」やり方を紹介します (I'll introduce how to deal with such things) Public/公開情報 -7-
  • 8. 標準入出力(STDIN/STDOUT)  コマンドラインオプション (commandline options)  時刻(system clock)  DB  etc. Public/公開情報 -8-
  • 9. Tests for STDIN/STDOUT  Principle – 内部のロジックが良くテストされているなら、無理して実施する必 要は無い(If internal logic is well tested, It is no need to test STDIN/STDOUT forcefully)  Example Situation – コマンドラインツールのテストをしたい(want to test command-line tool) – 外部モジュールの中間出力が見たいが、その内容が標準出力 を使っている(middle output for external modules, but the output is printed in STDOUT/STDERR) – warn/carp の内容を確認したい(want to check output by warn/carp)  Solution – use IO::Scalar – use Capture::Tiny(for STDOUT/STDERR) – use IO::Capture::STDOUT/STDERR – tie STDIN/STDOUT/STDERR Public/公開情報
  • 10. Automated Testing (2)  Ex 1)using IO::Scalar and capture STDIN #!/usr/bin/perl -w use strict; use warnings; use Test::More; sub add { my $result = 0; while( <STDIN> ) { chomp; $result += $_; } return $result; } subtest 'add', sub { my $inputs = "1n2n3n"; # input from STDIN open my $stdin_fh, '<', $inputs; local *STDIN = *$stdin_fh; # replace default STDIN is( add(), 6 ); }; done_testing(); Public/公開情報 - 10 -
  • 11. Automated Testing (2)  Ex 2) using Capture::Tiny(for STDOUT/STDERR) #!/usr/bin/perl -w use strict; use warnings; use Test::More; use Capture::Tiny qw(capture); sub add { my($a, $b) = @_; print $a + $b; } my ($stdout) = capture { add(1, 2); }; is($stdout, 3); done_testing(); – 簡単に使えるので、Test::Warn の代用とするのも良いと思う (I think it's good idea to use this module alternate for Test::Warn) Public/公開情報 - 11 -
  • 12. 標準入出力(STDIN/STDOUT)  コマンドラインオプション (commandline options)  時刻(system clock)  DB  etc. Public/公開情報 - 12 -
  • 13. Tests for command-line args  Principle – 内部のロジックが良くテストされているなら、無理して実施する必 要は無い(If internal logic is well tested, It is no need to test STDIN/STDOUT forcefully)  Example Situation – コマンドラインツールのテストをしたい(want to test command-line tool) – GetOpt::* を使わず、自前でオプション解析しているのを直したい (want to fix because it has self-implemented command-line args analysis)  Solution – @ARGV を書き換える Public/公開情報
  • 14. Tests for command-line args  Ex) testing command-line args #!/usr/bin/perl -w use strict; use warnings; use Test::More; our $a_str = undef; # 本当は別 package にある / in real case, this subroutine is defined in other package sub read_args { while ( $_ = shift @ARGV ) { if ( $_ =~ /^-a$/ ) { $a_str = shift @ARGV; } # ... other option analyses are follows } } subtest 'a option with arg test', sub { $a_str = undef; local @ARGV = ("-a", "a_value"); #ここにオプションを指定 / passes options here read_args(); is($a_str, "a_value"); }; done_testing(); Public/公開情報 - 14 -
  • 15. 標準入出力(STDIN/STDOUT)  コマンドラインオプション (commandline options)  時刻(system clock)  DB  etc. Public/公開情報 - 15 -
  • 16. Tests for system clock  Principle – 時刻に依存せずテストが書かれるべき(Tests should be written in no depencency to system clock)  Example Situation – ログの日付フォーマットが正しいかチェックしたい(want to test datetime format in logs) – ロット番号など、日付によって処理内容が変わるものをテストした い(want to test what changes depending on datetime such as lot-no)  Solution – 時刻を改竄する(alter perl's system clock) • use Test::MockTime • use Time::Mock • CORE::GLOBAL::time() を書き換える(override CORE::GLOBAL::time) Public/公開情報
  • 17. Tests for system clock  Ex) Test::MockTime #!/usr/bin/perl use strict; use warnings; BEGIN { $ENV{TZ} = 'JST' } use Test::MockTime qw(set_fixed_time); use Test::More; use POSIX qw(strftime); sub some_lot_no { return strftime("%Y%m%d-%H%M%S", localtime()); } set_fixed_time('2009-03-23T11:22:33'); is( some_lot_no(), '20090323-112233'); done_testing(); Public/公開情報 - 17 -
  • 18. 標準入出力(STDIN/STDOUT)  コマンドラインオプション (commandline options)  時刻(system clock)  DB  etc. Public/公開情報 - 18 -
  • 19. Tests for DB  Principle – 基本はモック(DBD::Mock)を使うべき(Mock should be used) – ビジネスロジックと DB は切り離すべき(Business logics and DB should be separated)  Example Situation – ストアドプロシージャをテストしたい(want to test stored procedure) – ORM を使わず、生の DBI を使っているので SQL をテストしたい (want to test SQL because we don't use ORM)  Solution – データを流し込む(load data into DB) – Test::mysqld + something • Test::Fixture::DBI • Test::DBUnit • Test::DataLoader::MySQL Public/公開情報
  • 20. 標準入出力(STDIN/STDOUT)  コマンドラインオプション (commandline options)  時刻(system clock)  DB  etc. Public/公開情報 - 20 -
  • 21. exit measures(1)  テスト中に exit が呼ばれると、意図せずテストが通ってしまう (If exit() is called, tests are passed accidentally)  Example Situation – 他人のコードを引き継いだ際(when takeover someone's code) – ライブラリがエラー処理後に exit を呼んでいた(library routine calls exit after error handling)  Solution – exit()の上書き(override exit) – exit を使っている関数/メソッドの上書き(override subroutine/method which uses exit) Public/公開情報 - 21 -
  • 22. Exit measures(2)  Ex1) exit() causes problem #!/usr/bin/perl -w use strict; use warnings; use Test::More 'no_plan'; my $important_value = ''; sub evil_operation { $important_value = "aaa"; exit 0; } ok(1); evil_operation(); is( $important_value, '' ); % prove exit.t exit.t .. ok All tests successful. Files=1, Tests=1, 0 wallclock secs ( 0.04 usr 0.00 sys + 0.01 cusr 0.01 csys = 0.06 CPU) Result: PASS – This test successes unexpectedly Public/公開情報 - 22 -
  • 23. Exit measures(3)  Ex2) measured exit() call #!/usr/bin/perl -w use strict; use warnings; use Test::More 'no_plan'; BEGIN { *CORE::GLOBAL::exit = sub { die 'unexpected exit called!' } } # ADD THIS! my $important_value = ''; sub evil_operation { $important_value = "aaa"; exit 0; } ok(1); evil_operation(); is( $important_value, '' ); % prove exit_measured.t exit_measured.t .. 1/? unexpected exit called! at exit_measured.t line 6. ...(snip) Result: FAIL – It's OK. It should be failed. Public/公開情報 - 23 -
  • 24. setUp/tearDown(1)  xUnit を使っていた人は setUp/tearDown が使いたいかも (xUnit users may want to use setup/tearDown) – それ Test::Class で出来るよ! (Test::Class enables it!) #!/usr/bin/perl -w use strict; use warnings; use Test::Class; MyTest->runtests(); package MyTest; use parent qw(Test::Class); use Test::More; sub set_up :Test(setup) { diag("setup"); } sub tear_down :Test(teardown) { diag("teardown"); } sub my_test :Test(1) { ok(1); #this is some test } sub my_test2 :Test(1) { is("1", "1"); #this is another test } Public/公開情報 - 24 -
  • 25. setUp/tearDown(2)  でも書き方が変わるのは面倒くさい (But it isn't good that how to write test is changed) – subtest + Hook::LexWrap #!/usr/bin/perl -w use strict; use warnings; use Test::More; use Hook::LexWrap; wrap 'subtest', pre => sub { diag("setup"); }, #alternate for setup post => sub { diag("teardown");} #alternate for teardown ; subtest 'my_test', sub { ok(1); }; subtest 'my_test2', sub { is("1", "1"); }; done_testing(); Public/公開情報 - 25 -
  • 26. setUp/tearDown(3)  実行例(execution example) % perl setup_teardown.t # setup setup called ok 1 1..1 ok 1 - my_test test called # teardown # setup teardown called ok 1 1..1 ok 2 - my_test2 # teardown 1..2 Public/公開情報 - 26 -
  • 27. Caution(1)  今回紹介したテクニックをプロダクションコード側で使わない (Don't use these techniques in production code) – プロダクションコードでモンキーパッチしたり、時間や CORE::GLOBAL::* を書き換えたり、@ARGV 書き換えた り、標準入出力捕まえたりしないこと (In production code, don't monkey-patch, don't alter system clock, don't replace CORE::GLOBAL::*, don't replace @ARGV, and don't capture STDIN/STDOUT) – テストでは有効なテクニックでも、プロダクションコードで使 うと妙なバグに振り回されるかもしれません (These techniques are useful in testcode, but you may encounter curious bugs if using it in production code) Public/公開情報 - 27 -
  • 28. Caution(2)  「テストしにくい部分を何とかしたい!」という考えは基本的には何 かが間違っています (I think it is wrong opinion such as 'I want to manage testcode which is hard to be written') – 段階的に直していくべき(It should be fixed gradually) – Mock 使うとか、他の部分をテストしてカバーするとか (using Mock or tests other parts to cover it) Public/公開情報 - 28 -
  • 29. Conclusion  テストしにくいものも、Perl だと結構なんとかなります (Sometimes it is hard to write testcode, but Perl provides power to make it possible)  「どうやったら、このテストしにくいコードを何とかできるか」を考え るのは結構楽しい (It is fun thinking about how to write testcode which is hard to be written) – とくにレガシーコードを相手にする場合は (especially for legacy codes)  テストを書きましょう!辛いテストでも Perl なら何とかな ります! (Let's write testcode! Perl enables you to provide power to write hard tests) Public/公開情報 - 29 -
  • 31. 質疑をうけて、ちょっとだけ補足(発表後に追記) exit で意図せずテストが成功する場合の話ですが、これは 「no_plan」にしているときのみ発生する事象です。 比較的新しい Test::More を使っていれば、done_testing() が使えるので、それを使うべきです。また、5.8.8 とかに 標準添付される Test::More だと done_testing()が使え ないバージョンなので、その場合は no_plan をそもそも 避けるべきです。 (sorry only in Japanese) Public/公開情報 - 31 -