【しばらく編集不可モードで運営します】 編集(管理者用) | 差分 | 新規作成 | 一覧 | RSS | FrontPage | 検索 | 更新履歴

IntroTestMore - A Perl Testing Tutorial

目次

A Perl Testing Tutorial

chromatic (http://wgz.org/chromatic/)


英語がそのまま残ってる部分は意味がわからなかっとこなので、 適当に補完してね。

■なぜテストするのか

  コードが仕様どおりの動きをすることを確認する。
  o 実装忘れの防止
  o アクシデントに対する動き
  o 全てのテストにパスすれば完成
  
  境界条件のチェック
  o 不正な入力をどう扱うか
  o イジワルな操作にどう対処するのか
  
  prevent (known) regressions
  o テストしていない機能はとつぜん動作しなくなるかもしれない
  o 修正されていないバグは再発しうる
  
  バグ報告をよりよいものにする
  o テストの出力は要所をついたエラーであること
  o 客先で簡単に発生(再現・追試)できること
    * 「これを動かした結果を送ってください」
  
  リファクタリングを可能にする
  o you break it, you know it right away
  o 副作用が明らかになっている
  o well-tested code tends to be more modular
    * 以前これと似た動作のテストをしたモジュールを作ったぞ。
      それらは単に表面上の関係があるだけだ。2回目のテストは
      ほとんど同じだから、それを含んだ親のクラスが必要だ。
      既存のテストのほとんどを親クラスに移すと、それを継承する
      クラスはもっとシンプルでとても短くなる。それらのテストも
      さらにシンプルだ。これはメンテするコードが少なくなって、
      書くコードも少ないし、新しい機能を追加する際の柔軟性も高
      まる。
  
  API を改善する
  o テストするのが難しいコードは使うのも難しい
  o テストコードを最初に書くことで、コーディングを始める前に
    インターフェースを使う(考える)ことになる
    * インターフェースがシンプルであればテストも楽
    * 小さな関数は再利用が楽
  
  自動化を進める
  o easy to test code is usually well-decoupled
  o easy to test is, by definition, scriptable
  
  メンテを楽にする
  o テストはセーフティーネット(転ばぬ先の杖?)である
    * 今わからなくても、あとでもう一度繰り返します
  o よくできたテストは短いドキュメントにもなる
    * ソフトウェアがどんな仕様をもつべきかをデモする
    * コードがどうふるまうべきかをデモする
  

■恐れないで!

  
  恐れないで!
  o 良いテストコードを書くことは良いコードを書く練習になる
  o コードが書ければテストも書けるよね
  o いいコードが書ければ、良いテストを書くことも教えられている
    わけだ
  
  テストは精神的にもいい
  o テストコードを書くこと(とりわけテストファースト)は、テス
    トを書きやすくする(コードを書きやすくするの間違いか?)
  o よくテストされたコードならハックしてもおもしろい
  o テストファースト・プログラミングはすばやいフィードバックが
    可能。
  o そしてそれはたいてい、前進的なフィードバックだ
  
  Perl はそれを簡単にする
  o ExtUtils::MakeMaker とテストされるもの
  o Test::* モジュールは Test::Harness が必要とするものを隠して
    (援助して)くれる
  o if lasy dopes like us can do it, you can too!
  

■ベーシックテスト

  "ok" について
  print "ok";
  print "not ok";
  o 全てのテストの基礎
  o シンプルな二値の条件
  o 通常、連番が振られる
  
  さらに "ok" について
  o なぜ print することがムダなのか
    * サブルーチンを使うのと同じく、同じ引数(コードの二重化)
    * 手動で番号を振る
    * 自分でやらないと診断メッセージが出ない
      なにがマッチしたのか
      何がマッチしなかったのか
      どのテストがどこで失敗したのか
    * もっと複雑な構成に対する組み込みの手続きがない
    * メンテが大変
  
  ok() 関数
  ok(1==1);
  o Test::Simple, Test::More, Test::Builder の基礎
  o シンプルな2値の条件
  o なぜそれがいいのか
    * 同じコードを書く手間が省ける
    * テストに名前がつけられる
    * テストに説明を加えることでもっとクリアになる
    * ポータビリティの心配がなくなる
    

■Test::Simple とその API

  テストを計画する
  o テストのエラーの過不足を把握する助けになる
  o どのテストが失敗したかを見つけやすくする
  o Test::Harness に必要
  use Test::Simple tests => 10;
  use Test::Simple 'no_plan';
  
  tests => n
  o Test::Harness 用に 1..n の番号を出力する
  o テストコードが完成したときに定義すべき
  
  no_plan
  o 1 から実際のテストの終わりまで連番が出力される
  o どれだけのテストがあるかわからないとき
  o モジュールが開発中のときに便利
  o モジュールを配布するときは便利ではない
  
  ok()
  o 引数はスカラー
  o 最初の引数はつねに真か偽
  o 2番目の引数はテストの名前でオプショナル
  o 条件はテストの名前に対応したコード
  o テストの名前は条件を表す宣言になっていること
  
  ok() が成功したとき
  ok( 1, '1 should be true' );
  ok 1 - 1 should be true
  
  ok() が失敗したとき
  ok( 0, '0 should be true' );
  not ok 2 - 0 should be true
  # Failed test (demotest.pl at line 17)
  
  成功はグレーゾーンではない(明らかなテスト)
  o 成功または失敗だけで、多分はない
  o もっとも小さな単位でテストせよ
  o 一度にひとつだけテストせよ
  o 静かに成功させる(成功時は余計なメッセージが出なくなる?)
    * エラーをなくす
    * 警告をなくす
    * そうすべきなら、エラーや警告のためのテストもすること
  
  成功は全て成功か、全て失敗かのどちらか(test suite)
  o 予想される失敗を全てチェックする
    * undermine confidence in the code
    * clutter up screens and logs
    * hide new problems
  o 変更を小さく保つことで、ダメージを局所にとどめる
  o テストしつづけることで、ダメージを局所にとどめる
  o プラットフォームに依存したテストを別にする方法がある

■開発過程にテストを盛り込む

  
  catching regressions
  o バグの多くは新しいものではなく、繰り返し起こっていた
  o なぜそれがまた起こったのか
  o 新しいバグが出たら、それに対するテストを書く
  
  テストファースト
  o teeny, tiny, mini-iterations
  o それぞれの仕事を二値の問題に分ける
  o 「次に必要な機能は何だろう?」
    * 全体の仕事の中の最も小さな要素
    * 一度にひとつの小さな段階
  
  テストファーストに対する二つの疑問
  o 「この機能が働いていることをどうやって証明する?」
    * その機能が働いていないと失敗するであろう、もっとも
      シンプルなテスト
    * テストは失敗しなければならない
  o 「そのテストをパスするのに書かなければならない最小のコードは?」
    * テストがシンプルなほど、必要なコードもシンプルになる
    * テストは今成功しなければならない
  o この手続きは既知の良いコードと包括的な test suite を生み出す
  o 機能を全部作り終えた後、全体の test suite を試すのを忘れないこと
  
  テストできるようなコードを書く
  o 単位が小さいほど良い
  o 単位がシンプルなほど良い
  o test suite が良ければ、リファクタリングも簡単
  o テストを最初に書けばそれができるようになる!
  
  テストの配布
  o Test.pm なら配布が楽
    * ひとつのモジュールで、Test::Builder に依存しない
    * Test::More の API とコンパチ
  o Test::Simple suite はモジュールと一緒にされる
    * CGI.pm などがこれをやっている
  o Test::Simple は 5.8 に同梱
  o モジュールは Test::Simple に依存できる

■Test::More の API について

  
  Test::Simple を卒業して Test::More へ、あるいは
  なぜ ok() は不十分なのか
  o それが成功か失敗かであるにしても、データは 1bit だけ
  o デバッグのための情報が少ない
    * 何が得られたのか?
    * 何を受け取ったのか?
    * "add a print at line 43" なんていう E-mail を本当に
      受け取りたいと思ってるの?
    * 真の横着は良いテストの兆候
  
  is() - 一致
  o 「これら二つは同じか」を調べる
  o 型を考えてくれる
  o undef の場合
  
  is() が成功したとき
  is( 2 + 2, 4, 'Two plus two should be four' );
  ok 1 - Two plus two should be four
  
  is() が失敗したとき
  is( 2 + 2, 5, 'Two plus two should be five' );
  not ok 2 - Two plus two should be five
  # Failed test (demotest.pl at line 8)
  # got: '4'
  # expected: '5'
  
  isnt - 不一致
  o 「これら二つは違っているか」を調べる
  o is() の反対
  
  isnt() が成功したとき
  isnt( 2 + 2, 5, 'Two plus two should not be five' );
  ok 3 - Two plus two should not be five
  
  isnt() が失敗したとき
  isnt( 2 + 2, 4, 'Two plus two should not be four' );
  not ok 4 - Two plus two should not be four
  # Failed test (demotest.pl at line 10)
  # '4'
  # ne
  # '4'
  1..4
  
  like() - 一致
  o 文字列に対して正規表現を試す
  o 最初の引数は文字列
  o 二番目の引数は正規表現
    * Perl のバージョンによっては qr// でコンパイル済み
      正規表現
    * そうでなければ正規表現を表す /foo/ のような文字列
  
  like() が成功したとき
  like( 'foobar', qr/fo+/,
  '+ quantifier should match one or more' );
  ok 5 - + quantifier should match one or more
  
  like() が失敗したとき
  like( 'fbar', qr/fo+/,
  '+ quantifier should match zero or more' );
  not ok 6 - + quantifier should match zero or more
  # Failed test (demotest.pl at line 12)
  # 'fbar'
  # doesn't match '(?-xism:fo+)'
  
  unlike() - 不一致
  o like() の反対
  o 使い方は同じ
  
  unlike() が成功したとき
  unlike( 'foobar', qr/baz/, 'foobar should not match baz' );
  ok 7 - foobar should not match baz
  
  unlike() が失敗したとき
  unlike( 'foobar', qr/bar/, 'foobar should not match bar' );
  not ok 8 - foobar should not match bar
  # Failed test (demotest.pl at line 14)
  # 'foobar'
  # matches '(?-xism:bar)'
  1..8
  
  use-ok() の動作
  o (これらは全てそれ自身のテストの名前を出力します)
  o モジュールがコンパイルされて、import に成功したかを調べる
  o モジュールをロードして、import() を適用する
  o コンパイル時ではなく実行時(ランタイム)に動作する
  o デフォルトのテスト名を出力するので、名前を指定する必要はない
  o import 時の引数も渡せる
  
  
  use_ok() が成功したとき
  use_ok( 'Test::More' );
  ok 11 - use Test::More;
  
  use_ok() が失敗したとき
  use_ok( 'No::Module' );
  not ok 12 - use No::Module;
  # Failed test (demotest.pl at line 20)
  # Tried to use 'No::Module'.
  # Error: Can't locate No/Module.pm in @INC (@INC
  # contains: /usr/lib/perl5/5.6.1/i386-linux
  # /usr/lib/perl5/5.6.1
  # /usr/lib/perl5/site_perl/5.6.1/i386-linux
  # /usr/lib/perl5/site_perl/5.6.1
  # /usr/lib/perl5/site_perl .) at (eval 6) line 2.
  
  require_ok の動作
  o モジュールがコンパイルされたかどかを調べる
  o モジュールをロードするが、import() はしない
  o これもデフォルトのテスト名を出力する
  
  require_ok() が成功したとき
  require_ok( 'Test::Simple' );
  ok 13 - require Test::Simple;
  
  require_ok() が失敗したとき
  require_ok( 'No::Module' );
  not ok 14 - require No::Module;
  # Failed test (demotest.pl at line 23)
  # Tried to require 'No::Module'.
  # Error: Can't locate No/Module.pm in @INC (@INC
  # contains: /usr/lib/perl5/5.6.1/i386-linux
  # /usr/lib/perl5/5.6.1
  # /usr/lib/perl5/site_perl/5.6.1/i386-linux
  # /usr/lib/perl5/site_perl/5.6.1
  # /usr/lib/perl5/site_perl .) at (eval 8) line 2.
  
  isa_ok() の動作
  o オブジェクトがリファレンスかどうかを調べる
  o オブジェクトになる
    * ただしクラスではない
    * コンストラクタに関する三つのテストをやってくれる
      何かしら定義済みの値を返したか?
      それはリファレンスか?
      そのリファレンスはオブジェクトか?
  o respects inheritance
  o リファレンスになる(スカラー,アレー,ハッシュ,io,
    コード)(io ってファイルハンドルのこと?)
  
  isa_ok() が成功したとき
  isa_ok( [], 'ARRAY' );
  ok 15 - The object isa ARRAY
  
  
  isa_ok() が失敗したとき
  isa_ok( {}, 'IO::Socket' );
  not ok 16 - The object isa IO::Socket
  # Failed test (demotest.pl at line 26)
  # The object isn't a 'IO::Socket' it's a 'HASH'
  
  can_ok の動作
  o オブジェクトが名前づけされた動作をもっているかを調べる(いみふめ)
  o オブジェクトまたはクラスになる
  o 一度のテストで、複数の関数やメソッド名を渡す
  o 複数のテストでは、それらを全部テストするように
    can_ok() の呼び出しを行う
  o respects inheritance, but falls afoul of AUTOLOAD() just
    like UNIVERSAL::can() does
  
  can_ok() が成功したとき
  can_ok( 'Test::More', 'can_ok' );
  ok 17 - Test::More->can('can_ok')
  
  can_ok() が失敗したとき
  can_ok( 'Test::More', 'autotest' );
  not ok 18 - Test::More->can('autotest')
  # Failed test (demotest.pl at line 29)
  # Test::More->can('autotest') failed
  
  skip() - 制御する
  o 指定した番号のテストをスキップする
  o 通常、絶対に成功しないようなテストに対して使う
  o スキップする理由と番号を指定する
  o ブロックには SKIP というラベルをつける
  o スキップするための条件が必要
  
  skip() の動作
  SKIP: {
  skip( 'Never on a Sunday', 2 ) if (localtime)[6] == 0;
  can_ok( 'Perl::Porter', 'buy_beer' );
  can_ok( 'Perl::Porter', 'wear_short_pants' );
  }
  ok 21 # skip Never on a Sunday
  ok 22 # skip Never on a Sunday
  
  todo() - 制御
  o 指定した番号のテストは失敗することを明記する
  o 通常、失敗したときに使う
    * 奮闘中!
    * まだ実装が完全に終わってないとき
    * バグがとりきれてないとき
    * don't let these add up: they undermine the suite
  o ブロックには TODO というラベルをつける
  o 局所変数 $TODO に名前を設定
  
  todo() の動作
  TODO: {
  local $TODO = 'The alien overlords have not arrived';
  is( $smallpox->status(), 'ready', 'Smallpox ready' );
  ok( @cow_decoys > 1000, 'Bait ready...' );
  }
  not ok 23 - Smallpox ready # TODO The alien overlords have
  not arrived
  # Failed (TODO) test (demotest.pl at line 46)
  # got: 'replenishing'
  # expected: 'ready'
  not ok 24 - Bait ready... # TODO The alien overlords have
  not arrived
  # Failed (TODO) test (demotest.pl at line 47)
  
  diag() - 制御
  o 診断メッセージを表示する
  o テストハーネスに影響を与えないことを保証
  o 連番も成功も失敗も出力しない
  o トリッキーなテストにコメントをつけるのに便利
  
  diag() の動作
  diag( "Now that you know a bit about test writing,\n",
  "you have no excuse not to test." );
  # Now that you know a bit about test writing,
  # you have no excuse not to test.
  
  pass() - 制御
  o 無条件に成功したテストを出力する
  o テストの名前を与える -- that's it
  
  pass() の動作
  
  pass( 'Executive fiat should work' );
  ok 19 - Executive fiat should work
  
  fail() - 制御
  o 無条件に失敗したテストを出力する
  o これもテストの名前を与える -- that's it
  
  fail() の動作
  fail( '... but we live in a meritocracy' );
  not ok 20 - ... but we live in a meritocracy
  # Failed test (demotest.pl at line 32)


http://magnonel.guild.net/~schwern/talks/Test_Tutorial/Test-Tutorial.pdf
  のほうはもうすこしおもしろい。(英語 100ページ)