Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

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

3,898 views

Published on

  • Be the first to comment

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

  1. 1. あんなテスト・こんなテスト (This and That about testing) 土田 拓也(Takuya Tsuchida) @tsucchi 2011/10/15 id: tsucchi1022 エレクトロニクス事業本部Public/公開情報 -1-
  2. 2. Abstract  テストの話をします (Ill be talking about testing)  とくに「テストしにくい部分をどのようにテストするか」について話し ます (Especially, Ill talk about how to test the part which is hard to test)Public/公開情報 -2-
  3. 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/tsucchiPublic/公開情報 -3-
  4. 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. 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. 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. 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.  これらを、「なんとかする」やり方を紹介します (Ill introduce how to deal with such things)Public/公開情報 -7-
  8. 8.  標準入出力(STDIN/STDOUT)  コマンドラインオプション (commandline options)  時刻(system clock)  DB  etc.Public/公開情報 -8-
  9. 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/STDERRPublic/公開情報
  10. 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. 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 its good idea to use this module alternate for Test::Warn)Public/公開情報 - 11 -
  12. 12.  標準入出力(STDIN/STDOUT)  コマンドラインオプション (commandline options)  時刻(system clock)  DB  etc.Public/公開情報 - 12 -
  13. 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. 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. 15.  標準入出力(STDIN/STDOUT)  コマンドラインオプション (commandline options)  時刻(system clock)  DB  etc.Public/公開情報 - 15 -
  16. 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 perls system clock) • use Test::MockTime • use Time::Mock • CORE::GLOBAL::time() を書き換える(override CORE::GLOBAL::time)Public/公開情報
  17. 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. 18.  標準入出力(STDIN/STDOUT)  コマンドラインオプション (commandline options)  時刻(system clock)  DB  etc.Public/公開情報 - 18 -
  19. 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 dont use ORM)  Solution – データを流し込む(load data into DB) – Test::mysqld + something • Test::Fixture::DBI • Test::DBUnit • Test::DataLoader::MySQLPublic/公開情報
  20. 20.  標準入出力(STDIN/STDOUT)  コマンドラインオプション (commandline options)  時刻(system clock)  DB  etc.Public/公開情報 - 20 -
  21. 21. exit measures(1)  テスト中に exit が呼ばれると、意図せずテストが通ってしまう (If exit() is called, tests are passed accidentally)  Example Situation – 他人のコードを引き継いだ際(when takeover someones code) – ライブラリがエラー処理後に exit を呼んでいた(library routine calls exit after error handling)  Solution – exit()の上書き(override exit) – exit を使っている関数/メソッドの上書き(override subroutine/method which uses exit)Public/公開情報 - 21 -
  22. 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 unexpectedlyPublic/公開情報 - 22 -
  23. 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 – Its OK. It should be failed.Public/公開情報 - 23 -
  24. 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. 25. setUp/tearDown(2)  でも書き方が変わるのは面倒くさい (But it isnt 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. 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..2Public/公開情報 - 26 -
  27. 27. Caution(1)  今回紹介したテクニックをプロダクションコード側で使わない (Dont use these techniques in production code) – プロダクションコードでモンキーパッチしたり、時間や CORE::GLOBAL::* を書き換えたり、@ARGV 書き換えた り、標準入出力捕まえたりしないこと (In production code, dont monkey-patch, dont alter system clock, dont replace CORE::GLOBAL::*, dont replace @ARGV, and dont capture STDIN/STDOUT) – テストでは有効なテクニックでも、プロダクションコードで使 うと妙なバグに振り回されるかもしれません (These techniques are useful in testcode, but you may encounter curious bugs if using it in production code)Public/公開情報 - 27 -
  28. 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. 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 なら何とかな ります! (Lets write testcode! Perl enables you to provide power to write hard tests)Public/公開情報 - 29 -
  30. 30. Public/公開情報 - 30 -
  31. 31. 質疑をうけて、ちょっとだけ補足(発表後に追記) exit で意図せずテストが成功する場合の話ですが、これは 「no_plan」にしているときのみ発生する事象です。 比較的新しい Test::More を使っていれば、done_testing() が使えるので、それを使うべきです。また、5.8.8 とかに 標準添付される Test::More だと done_testing()が使え ないバージョンなので、その場合は no_plan をそもそも 避けるべきです。 (sorry only in Japanese)Public/公開情報 - 31 -

×