SlideShare une entreprise Scribd logo
1  sur  83
YAPC::ASIA 2012




What is wrong
on Test::More?
Test::Moreが抱える問題点とその解決策

makoto kuwata <kwa@kuwata-lab.com>
http://www.kuwata-lab.com/
2012-09-27 (Fri)




                 copyright(c) 2012 kuwata-lab.com all rights reserved.
Agenda
✦ Testing   without spec
 仕様を書かずにテストしてる

✦ Not   structured tests
 テストが構造化されてない

✦ Needs     test plan
 事前にテストプラン (=テスト数) を必要とする

✦ No   fixture feature
 フィクスチャ機能がない

✦ Hard   to distinguish assertions
 どれがアサーションなのか分かりにくい


                copyright(c) 2012 kuwata-lab.com all rights reserved.
Section 1:

Testing without spec
仕様を書かずにテストしている




             copyright(c) 2012 kuwata-lab.com all rights reserved.
Point



    Write your test
    according to spec,
    not your code.
    テストは、コードではなく仕様をもとに書け。



        copyright(c) 2012 kuwata-lab.com all rights reserved.
Sample: Test::More (Perl)

use	 Test::More	 tests	 =>	 4;

is	 f(0),	 0;
is	 f(1),	 1;
is	 f(2),	 1;
is	 f(3),	 2;




                copyright(c) 2012 kuwata-lab.com all rights reserved.
Sample: RSpec (Ruby)

describe	 'f()'
	 	 it	 "calculates	 fibonacchi	 sequence"	 do
	 	 	 	 f(0).should	 ==	 0
	 	 	 	 f(1).should	 ==	 1
	 	 	 	 f(2).should	 ==	 1
	 	 	 	 f(3).should	 ==	 2
	 	 end
end




              copyright(c) 2012 kuwata-lab.com all rights reserved.
Sample: unittest (Python)

import	 unittest

class	 FooTest(unittest.TestCase):
	 	 def	 test_calculates_fiboacchi_seq(self):
	 	 	 	 """Calculates	 Fibonacchi	 sequence"""
	 	 	 	 self.assertEqual(0,	 f(0))
	 	 	 	 self.assertEqual(1,	 f(1))
	 	 	 	 self.assertEqual(1,	 f(2))
	 	 	 	 self.assertEqual(2,	 f(3))




             copyright(c) 2012 kuwata-lab.com all rights reserved.
Goal of test


✦ Test::More:

 Does that code run correctly?
 そのコードは意図した通りに動くか?


✦ RSpec:

 Does that code satisfy the spec?
 そのコードは仕様を満たしているか?




                copyright(c) 2012 kuwata-lab.com all rights reserved.
Difference between Test::More and RSpec

##	 Test::More
like	 $html,	 qr'<h3>Hello</h3>';


                                Higher-level information
                                            より高水準の情報
##	 RSpec
it	 "contains	 section	 title"	 do
	 	 html.should	 =~	 %r'<h3>Hello</h3>'
end


              copyright(c) 2012 kuwata-lab.com all rights reserved.
Solution: spec()
                                                  https://gist.github.com/3797929

sub	 spec	 {
	 	 my	 ($text,	 $block)	 =	 @_;
	 	 $block->();
}

##	 usage
spec	 "page	 contains	 section	 title",	 sub	 {
	 	 ok(render()	 =~	 qr`<h1>Hello</h1>`);
};




               copyright(c) 2012 kuwata-lab.com all rights reserved.
Spec First

Step 1. Write specifications
spec	 "...specification1...";
spec	 "...specification2...";
 	 	 	 #=>	 not	 ok	 1	 -	 ...spec...	 #	 TODO
 	 	 	 #=>	 not	 ok	 2	 -	 ...spec...	 #	 TODO

Step 2. Add assertions according to spec
spec	 "...specification...",	 sub	 {
	 	 assertion1;
	 	 assertion2;
};
                copyright(c) 2012 kuwata-lab.com all rights reserved.
Solution: spec()
                                                  https://gist.github.com/3797939

sub	 spec	 {
	 	 my	 ($text,	 $block)	 =	 @_;
	 	 return	 $block->()	 if	 $block;
	 	 TODO:	 {
	 	 	 	 local	 $TODO	 =	 ":	 not	 implemented	 yet";
	 	 	 	 ok(undef);
	 	 }
}




               copyright(c) 2012 kuwata-lab.com all rights reserved.
Meaning of output lines
As is - 'ok' when assertion passed, 'not ok' when
        failed
       アサーションが成功したらok、失敗したらnot ok

ok	 1	 -	 assertion1
not	 ok	 2	 -	 assertion2

To be - 'ok' when spec satisfied, 'not ok' when not
       コードが仕様を満たしたらok、満たさなければnot ok

ok	 1	 -	 specification1
not	 ok	 2	 -	 specification2


              copyright(c) 2012 kuwata-lab.com all rights reserved.
Spec : Assertion = 1 : N
✦A specification can contain some
 assertions
 1つの仕様に複数のアサーションを書いてよい

spec	 "returns	 pair	 of	 integer",	 sub	 {
	 	 is	 scalar(@$ret),	 2;
	 	 like	 $ret->[0],	 qr/^d+$/;
	 	 like	 $ret->[1],	 qr/^d+$/;
};




              copyright(c) 2012 kuwata-lab.com all rights reserved.
Spec : Assertion = 1 : N

As is                    Output lines per assertion
                               アサーションごとに出力行
ok	 1
ok	 2
ok	 3

To be
ok	 1	 -	 returns	 pair	 of	 integer

                              Output lines per spec
                                        仕様ごとに出力行


              copyright(c) 2012 kuwata-lab.com all rights reserved.
Solution: OK()
                                                  https://gist.github.com/3797954

sub	 OK	 {
	 	 my	 ($expr)	 =	 @_;
	 	 unless	 ($expr)	 {
	 	 	 	 my	 ($pkg,	 $file,	 $lineno)	 =	 caller();
	 	 	 	 die	 "AssertionFailed"
	 	 	 	 	 	 	 ."	 at	 $file	 line	 $lineno.n";
	 	 }
}




               copyright(c) 2012 kuwata-lab.com all rights reserved.
Solution: spec()
                                                            https://gist.github.com/3797954
my	 $spec	 =	 undef;
my	 $num	 =	 0;

sub	 spec	 {
	 	 my	 ($text,	 $block)	 =	 @_;
	 	 $spec	 =	 $text;
	 	 $num++;
	 	 eval	 {	 $block->();	 };
	 	 my	 $err	 =	 $@;
	 	 if	 (!	 $err)	 {
	 	 	 	 print	 "ok	 $num	 -	 $textn";
	 	 }	 else	 {
	 	 	 	 print	 "not	 ok	 $num	 -	 $textn";
	 	 	 	 $err	 =~	 s/^/#	 	 	 /mg;
	 	 	 	 $err	 .=	 "n"	 if	 $err	 !~	 /nz/;
	 	 	 	 print	 STDERR	 $err;
	 	 }
}



                         copyright(c) 2012 kuwata-lab.com all rights reserved.
Conclusion of this section
✦ Write   test based on spec, not on code
 テストは、コードに対してではなく、仕様に対して書く

✦ 'Spec   specified?' instead of 'Run correctly?'
 「正しく動作するか?」ではなく「仕様を満たしているか?」

✦ Spec    first, assertion second
 仕様を先に書いて、そのあとにアサーションを書く

✦ Spec    : Assertion = 1 : N
 1つの仕様が複数のアサーションを含んでよい

✦ Output   line per spec, not assertion
 出力行のok / not okはアサーション単位ではなく仕様単位に出す


                copyright(c) 2012 kuwata-lab.com all rights reserved.
Section 2:

Not structured tests
テストが構造化されてない




             copyright(c) 2012 kuwata-lab.com all rights reserved.
Point




Test should have structure.
Because spec has structure.
テストには構造がある。なぜなら仕様に構造があるから。




        copyright(c) 2012 kuwata-lab.com all rights reserved.
Sample: Specification document

  クラス:Calendar
  メソッド:isLeapYear(int year): boolean
  動作詳細:
   ・100で割り切れる場合、
    ・400で割り切れる場合はtrueを返す
    ・それ以外はfalseを返す
   ・4で割り切れる場合はtrueを返す
   ・それ以外はfalseを返す


            copyright(c) 2012 kuwata-lab.com all rights reserved.
Test code as spec document



Is your test code available
as spec document?
そのコードは仕様書としてほんとに利用できるの?




           copyright(c) 2012 kuwata-lab.com all rights reserved.
Sample: Test::More

use	 Test::More	 tests	 =>	 2;
use	 Foo;

$foo	 =	 Foo->new();
is(Foo->new()->meth1(),	 11);
is(Foo->new()->meth2(),	 12);




              copyright(c) 2012 kuwata-lab.com all rights reserved.
Sample: RSpec
                               Test target (class, method, ...)
require	 'rspec'            テスト対象 (クラス、メソッド、…)
describe	 Foo	 do
	 	 describe	 '#bar()'	 do
	 	 	 	 context	 'when	 arg	 is	 provided'	 do
	 	 	 	 	 	 it	 "returns	 length"	 do
	 	 	 	 	 	 	 	 Foo.new.methd1([0,1,2]).should	 ==	 3
	 	 	 	 	 	 	 	 Foo.new.methd1([]).should	 ==	 0
	 	 	 	 	 	 end
	 	 	 	 end
	 	 end
end


               copyright(c) 2012 kuwata-lab.com all rights reserved.
Sample: RSpec
                                     Test condition or situation
require	 'rspec'                     条件や状況
describe	 Foo	 do
	 	 describe	 '#bar()'	 do
	 	 	 	 context	 'when	 arg	 is	 provided'	 do
	 	 	 	 	 	 it	 "returns	 length"	 do
	 	 	 	 	 	 	 	 Foo.new.methd1([0,1,2]).should	 ==	 3
	 	 	 	 	 	 	 	 Foo.new.methd1([]).should	 ==	 0
	 	 	 	 	 	 end
	 	 	 	 end
	 	 end
end


                copyright(c) 2012 kuwata-lab.com all rights reserved.
Sample: RSpec

require	 'rspec'
describe	 Foo	 do
	 	 describe	 '#bar()'	 do
	 	 	 	 context	 'when	 arg	 is	 provided'	 do
	 	 	 	 	 	 it	 "returns	 length"	 do
	 	 	 	 	 	 	 	 Foo.new.methd1([0,1,2]).should	 ==	 3
	 	 	 	 	 	 	 	 Foo.new.methd1([]).should	 ==	 0
	 	 	 	 	 	 end
	 	 	 	 end
	 	 end              Specification
                         仕様
end


                copyright(c) 2012 kuwata-lab.com all rights reserved.
Sample: unittest (Python)

import	 unittest

class	 FooTest(unitteset.TestCase):

	 	 def	 test_bar_1(self):
	 	 	 	 """returns	 length	 of	 arg	 passed"""
	 	 	 	 self.assertequal(3,	 Foo().bar([1,2,3]))

	 	 def	 test_bar_2(self):
	 	 	 	 """returns	 0	 when	 arg	 is	 not	 passed"""
	 	 	 	 self.assertEqual(0,	 Foo().bar())


               copyright(c) 2012 kuwata-lab.com all rights reserved.
Sample: Test::Unit2 (Ruby)

require	 'test/unit'
require	 'foo'

class	 FooTest	 <	 Test::Unit::TestCase
	 	 class	 MethTest	 <	 self
	 	 	 	 def	 test_returns_length_of_arg
	 	 	 	 	 	 n	 =	 Foo.new.bar([1,2,3])
	 	 	 	 	 	 assert_equal	 3,	 n
	 	 	 	 end
	 	 end
end
           ref: http://www.clear-code.com/blog/2012/4/25.html
             copyright(c) 2012 kuwata-lab.com all rights reserved.
Sample: subtest() (Test::More)

use	 Test::More	 tests=>1;

subtest	 "package	 Foo",	 sub	 {
	 	 plan	 tests=>1;
	 	 subtest	 "sub	 bar()",	 sub	 {
	 	 	 	 plan	 tests=>2;
	 	 	 	 ok	 (1+1	 ==	 2);
	 	 	 	 ok	 (1-1	 ==	 0);
	 	 };
};



               copyright(c) 2012 kuwata-lab.com all rights reserved.
Sample: subtest() (Test::More)

$	 perl	 homhom.t
1..1
	 	 	 	 1..1
	 	 	 	 	 	 	 	 1..2
	 	 	 	 	 	 	 	 ok	 1
	 	 	 	 	 	 	 	 ok	 2
	 	 	 	 ok	 1	 -	 sub	 bar()
ok	 1	 -	 package	 Foo




                 copyright(c) 2012 kuwata-lab.com all rights reserved.
Sample: subtest() (Test::More)

$	 perl	 homhom.t
1..1
	 	 	 	 1..1
	 	 	 	 	 	 	 	 1..2
	 	 	 	 	 	 	 	 ok	 1
	 	 	 	 	 	 	 	 ok	 2
	 	 	 	 ok	 1	 -	 sub	 bar()
ok	 1	 -	 package	 Foo




                 copyright(c) 2012 kuwata-lab.com all rights reserved.
Sample: subtest() (Test::More)

$	 perl	 homhom.t
1..1
	 	 	 	 1..1
	 	 	 	 	 	 	 	 1..2
	 	 	 	 	 	 	 	 ok	 1
	 	 	 	 	 	 	 	 ok	 2
	 	 	 	 ok	 1	 -	 sub	 bar()
ok	 1	 -	 package	 Foo




                 copyright(c) 2012 kuwata-lab.com all rights reserved.
Solution: subtest() alternatives
                                        Test target
                                           テスト対象
print	 "1..2n";
topic	 'package	 Foo',	 sub	 {
	 	 topic	 'sub	 meth1()',	 sub	 {
	 	 	 	 case_when	 'arg	 is	 passed',	 sub	 {
	 	 	 	 	 	 spec	 "1+1	 should	 be	 2",	 sub	 {
	 	 	 	 	 	 	 	 OK(1+1	 ==	 2);
	 	 	 	 	 	 };
	 	 	 	 	 	 spec	 "1-1	 should	 be	 0",	 sub	 {
	 	 	 	 	 	 	 	 OK(1-1	 ==	 0);
	 	 	 	 	 	 };
	 	 	 	 };          Condition or situation
	 	 };                    条件や状況
};
                copyright(c) 2012 kuwata-lab.com all rights reserved.
Solution: subtest() alternatives

$	 perl	 homhom.t
1..2
#	 *	 package	 Foo
#	 	 	 *	 sub	 meth1()
#	 	 	 	 	 -	 when	 arg	 is	 passed
ok	 1	 -	 1+1	 should	 be	 2
ok	 2	 -	 1-1	 should	 be	 0




                copyright(c) 2012 kuwata-lab.com all rights reserved.
Solution: subtest() alternatives
                                                   https://gist.github.com/3797976

my	 $depth	 =	 0;

sub	 topic	 {
	 	 	 	 my	 ($name,	 $block)	 =	 @_;
	 	 	 	 my	 $indent	 =	 '	 	 '	 x	 $depth;
	 	 	 	 print	 "#	 $indent*	 $namen";
	 	 	 	 $depth++;
	 	 	 	 $block->();
	 	 	 	 $depth--;
}



                copyright(c) 2012 kuwata-lab.com all rights reserved.
Solution: subtest() alternatives
                                                  https://gist.github.com/3797976

my	 $depth	 =	 0;

sub	 case_when	 {
	 	 	 	 my	 ($condition,	 $block)	 =	 @_;
	 	 	 	 my	 $indent	 =	 '	 	 '	 x	 $depth;
	 	 	 	 print	 "#	 $indent-	 $conditionn";
	 	 	 	 $depth++;
	 	 	 	 $block->();
	 	 	 	 $depth--;
}



               copyright(c) 2012 kuwata-lab.com all rights reserved.
Conclution of this section
✦ Testhas structure, because spec has
 structure.
  テストには構造がある。なぜなら仕様に構造があるから。

✦ xUnit
     focuses on test automation,
 RSpec focuses on test structure.
  xUnitは自動化のための道具、RSpecは構造化のための道具

✦ Output
       of subtest() suck.
 Define your own subtest alternatives.
  subtest()は出力が残念すぎる。自前関数お勧め。


            copyright(c) 2012 kuwata-lab.com all rights reserved.
Section 3:

Needs test plan
事前にテストプラン (=テスト数) を必要とする




             copyright(c) 2012 kuwata-lab.com all rights reserved.
Sample: Test::More

use	 Test::More	 tests=>2;

is(1+1,	 2);
is(1-1,	 0);



$	 perl	 homhom.t
1..2
ok	 1
ok	 2


               copyright(c) 2012 kuwata-lab.com all rights reserved.
Sample: subtest()

use	 Test::More	 tests=>1;

subtest	 'package	 Foo',	 sub	 {
	 	 plan	 tests=>1;
	 	 subtest	 'sub	 meth()',	 sub	 {
	 	 	 	 plan	 test2=>1;
	 	 	 	 is(1+1,	 2);
	 	 	 	 is(1-1,	 0);
	 	 };
};



               copyright(c) 2012 kuwata-lab.com all rights reserved.
Pros of test plan
✦ Detect   unexpected test finishing
  テストの異常終了が検知できる

 • Compare number of ('ok' + 'not ok') with test
   plan
   出力されたokやnot okの数と、 宣言されたテスト個数とを比較

✦ Necessary   to report test progress
  テスト実行時に進行状況を知るのに必要

 • Especially for 'prove' command
  特にproveコマンドで


               copyright(c) 2012 kuwata-lab.com all rights reserved.
Cons of test plan
✦ Too   messy to keep currect value
  正しい値に更新し続けるのが面倒すぎる

 • Update test plan when you add assertions
   assertion関数を追加したら忘れずに更新

 • back to top when you add at the end of file
   ファイルの末尾にテストを追加したら、先頭に戻ってテスト数
  を更新




               copyright(c) 2012 kuwata-lab.com all rights reserved.
Sample: done_testing()

use	 Test::More	 tests=>2;

is(1+1,	 2);
is(1-1,	 0);
done_testing();


$	 perl	 homhom.t
ok	 1
ok	 2
1..2


              copyright(c) 2012 kuwata-lab.com all rights reserved.
done_testing() and subtest()

use	 Test::More	 tests=>1;

subtest	 "package	 Foo",	 sub	 {
	 	 subtest	 "sub	 meth1()",	 sub	 {
	 	 	 	 ok	 (1+1	 ==	 2);
	 	 	 	 ok	 (1-1	 ==	 0);
	 	 	 	 done_testing();
	 	 };
	 	 done_testing();
};
done_testing();
  (No need to call done_testing() in subtest() since Test::More 0.95_01)
                    copyright(c) 2012 kuwata-lab.com all rights reserved.
Solution: subtest() alternatives

$	 perl	 homhom.pl
	 	 	 	 	 	 	 	 ok	 1
	 	 	 	 	 	 	 	 ok	 2
	 	 	 	 	 	 	 	 1..2
	 	 	 	 ok	 1	 -	 sub	 meth1()
	 	 	 	 1..1
ok	 1	 -	 package	 Foo
1..1




                copyright(c) 2012 kuwata-lab.com all rights reserved.
Pros of done_testing()
✦ No   need to speicfy test plan!
  テスト数を指定しなくていい!




               copyright(c) 2012 kuwata-lab.com all rights reserved.
Cons of done_testing()
✦ Need   to expand TAP spec
  TAP仕様に拡張が必要

 • Simplicity of TAP has gone
   もはやTAPの簡易性は損なわれた

✦ 'Prove'   prints '?' as number of tests
  proveコマンドで全体のテスト数が「?」に

 • Degeneration of interface
   インターフェースとしては退化




                copyright(c) 2012 kuwata-lab.com all rights reserved.
Off Topic: Doubt about TAP
✦ If   TAP accepts test plan after running tests,
   テスト数がわかるのがテスト終了後でいいなら

  • 'End of test' indicatior is necessary for TAP, but
    test plan is not, is it?
       テストの終わりが分かる何かがあればテスト数いらなくね?

  • Index number of test is not necessary, is it?
       そもそも ok や not ok に通し番号いらなくね?

 ok	 1	 	 #	 	 ..spec..	 
 ok	 2	 	 #	 	 ..spec..
 <<TEST	 END>>

                 copyright(c) 2012 kuwata-lab.com all rights reserved.
The root cause of problem
✦ Impossibleto count number of tests
 before running tests
  テスト数を事前に数えられない

✦ To   be
  あるべき姿

 • Step1. Count and print number of tests
            テスト数を数えて出力

 • Step2. Run tests
            テストを実行


               copyright(c) 2012 kuwata-lab.com all rights reserved.
Solution: Intermediate data structure
As is:
is	 1+1,	 2;                                                           ok	 1
is	 1-1,	 0;                                                           ok	 2
                                                                       1..2

To be:
                                   topic
is	 1+1,	 2;                         topic                             1..2
is	 1-1,	 0;                                                           ok	 1
                                      spec                             ok	 2
                                      spec
          Easy to
         count tests
               copyright(c) 2012 kuwata-lab.com all rights reserved.
Cons of intermediate data structure
✦ Easyto count and filter tests before
 running tests
  テスト実行前にテストを数えたりフィルタするのが簡単にできる

✦ No   need to extend TAP specification
  TAPの仕様を拡張しなくてよい

 • No more done_testing()
   done_testing()なんていらない

 • No more nesting like subtest()
   subtest()のような入れ子対応はいらない


               copyright(c) 2012 kuwata-lab.com all rights reserved.
Sample: xUnit

class	 FooTest(TestCase):
	 	 def	 test1(self):	 ...
	 	 def	 test2(self):	 ...

##	 build	 intermediate	 data	 structure
suite	 =	 TestSuite()
suite.add(FooTest('test1'))
suite.add(FooTest('test2'))

##	 run	 tests
TestRunner().run(suite)


             copyright(c) 2012 kuwata-lab.com all rights reserved.
Solution: topic() and spec()
                                                   https://gist.github.com/3798000

topic	 'class	 Foo',	 sub	 {
	 	 topic	 'sub	 meth1()',	 sub	 {
	 	 	 	 case_when	 "arg	 is	 given",	 sub	 {
	 	 	 	 	 	 spec	 "1+1	 should	 be	 2",	 sub	 {
	 	 	 	 	 	 	 	 OK(1+1	 ==	 2);
	 	 	 	 	 	 };
	 	 	 	 };            Change to build tree
	 	 };
};
                   Traverse tree
run_all();


                copyright(c) 2012 kuwata-lab.com all rights reserved.
Solution: topic()
                                                 https://gist.github.com/3798000

my	 $NODES	 =	 [];

sub	 topic	 {
	 	 my	 ($name,	 $block)	 =	 @_;
	 	 my	 $node	 =	 {name=>$name,	 prefix=>'*',
	 	 	 	 	 	 	 	 	 	 	 	 	 	 children=>[]};
	 	 push	 @$NODES,	 $node;
	 	 my	 $bkup	 =	 $NODES;
	 	 $NODES	 =	 $node->{children};
	 	 $block->();
	 	 $NODES	 =	 $bkup;
}

              copyright(c) 2012 kuwata-lab.com all rights reserved.
Solution: spec()
                                                 https://gist.github.com/3798000

my	 $NODES	 =	 [];

sub	 spec	 {
	 	 my	 ($text,	 $block)	 =	 @_;
	 	 push	 @$NODES,	 [$text,	 $block];
}




              copyright(c) 2012 kuwata-lab.com all rights reserved.
Solution: _count_specs()
                                                    https://gist.github.com/3798000

sub	 _count_specs	 {
	 	 my	 ($nodes)	 =	 @_;
	 	 my	 $n	 =	 0;
	 	 for	 (@$nodes)	 {
	 	 	 	 if	 (ref($_)	 eq	 'HASH')	 {	 	 	 	 	 	 	 #	 topic
	 	 	 	 	 	 $n	 +=	 _count_specs($_->{children});
	 	 	 	 }	 elsif	 (ref($_)	 eq	 'ARRAY')	 {	 #	 spec
	 	 	 	 	 	 $n	 +=	 1;
	 	 	 	 }
	 	 }
	 	 return	 $n;
}

                 copyright(c) 2012 kuwata-lab.com all rights reserved.
Solution: run_all()
                                                          https://gist.github.com/3798000
sub	 run_all	 {
	 	 print	 "1..",	 _count_specs($NODES),	 "n";
	 	 _run_all($NODES,	 0,	 0);
}

sub	 _run_all	 {
	 	 my	 ($nodes,	 $depth,	 $num)	 =	 @_;
	 	 my	 $indent	 =	 '	 	 '	 x	 $depth;
	 	 for	 my	 $x	 (@$nodes)	 {
	 	 	 	 if	 (ref($x)	 eq	 'HASH')	 {	 	 	 	 	 	 #	 topic
	 	 	 	 	 	 print	 "#	 $indent$x->{prefix}	 $x->{name}n";
	 	 	 	 	 	 $num	 =	 _run_all($x->{children},	 $depth	 +	 1,	 $num);
	 	 	 	 }
	 	 	 	 elsif	 (ref($x)	 eq	 'ARRAY')	 {	 	 #	 spec
	 	 	 	 	 	 my	 ($text,	 $block)	 =	 @$x;
	 	 	 	 	 	 $num++;
	 	 	 	 	 	 _run_spec($text,	 $num,	 $block);
	 	 	 	 }
	 	 }
	 	 return	 $num;
}
                       copyright(c) 2012 kuwata-lab.com all rights reserved.
Conclustion in this seciton
✦ Don't   count tests manually. Use computer.
  テスト数を手動で数えるのはやめてコンピュータにさせよう

✦ Noneed to expand TAP specification.
 Both done_testing() and subtest() are
 wrong.
  TAPの仕様拡張は不必要。done_testing()もsubtest()も間違い

✦ Intermediate     data structure solves the
 problem.
  テストを表す中間データ構造を作れば万事解決


               copyright(c) 2012 kuwata-lab.com all rights reserved.
Section 4:

No fixture feature
フィクスチャ機能がない




             copyright(c) 2012 kuwata-lab.com all rights reserved.
What is fixture?

"A test fixture (also known as a test context) is the
set of preconditions or state needed to run a test.
The developer should set up a known good state
before the tests, and return to the original state
after the tests."
                                       http://en.wikipedia.org/wiki/XUnit


"テストを実行、成功させるために必要な状態や前提条件の集合
を、フィクスチャと呼ぶ。これらはテストコンテキストとも呼
ばれる。開発者はテストの実行前にテストに適した状態を整
え、テスト実行後に元の状態を復元することが望ましい。"
                                        http://ja.wikipedia.org/wiki/XUnit
               copyright(c) 2012 kuwata-lab.com all rights reserved.
Fixture method
✦ xUnit
 • setUp() / tearDown()

✦ RSpec
 • before() / after()

✦ Test::More
 • (nothing!)




                 copyright(c) 2012 kuwata-lab.com all rights reserved.
Fault of setUp/tearDown
All tests in a class must share a setUp/tearDown.
	 	 sub	 setUp	 {	 my	 ($self)	 =	 @_;
	 	 	 	 $self->man	 	 	 =	 User->new(gender=>'M');
	 	 	 	 $self->woman	 =	 User->new(gender=>'W');
	 	 }
	 	 sub	 test1	 {	 my	 ($self)	 =	 @_;
	 	 	 	 my	 $user	 =	 $self->man;
	 	 	 	 ...
	 	 }
	 	 sub	 test2	 {	 my	 ($self)	 =	 @_;
	 	 	 	 my	 $user	 =	 $self->woman;
	 	 	 	 ...
	 	 }
               copyright(c) 2012 kuwata-lab.com all rights reserved.
Fault of setUp/tearDown
Separate TestCase class?
package	 FooTestCase;
sub	 setUp	 {	 ...	 }
sub	 testFoo	 {	 ...	 }

package	 BarTestCase;
sub	 setUp	 {	 ...	 }
sub	 testBar	 {	 ...	 }


     No. Test structure should follow specification
     reason, not fixture reason.

              copyright(c) 2012 kuwata-lab.com all rights reserved.
Another approach on fixture
Define fixture method for each test data.
sub	 fx_man	 {
	 	 return	 User->new(gender=>'M');	 }
sub	 fx_woman	 {
	 	 return	 User->new(gender=>'W');	 }

spec	 "test	 for	 man",	 sub	 {
	 	 OK(fx_man()->{gender}	 eq	 'M');	 }
spec	 "test	 for	 woman",	 sub	 {
	 	 OK(fx_woman()->{gender}	 eq	 'W');	 }

      More flexible than setUp/tearDown

              copyright(c) 2012 kuwata-lab.com all rights reserved.
Cons of the approach

spec	 "returns	 length	 of	 file",	 sub	 {
	 	 my	 $file	 =	 fx_file("homhom");
	 	 OK(file_length($file)	 ==	 6);
	 	 unlink($file);
};


           Troublesome task to do teardown
           for each fixture data
           fixtureごとに忘れずに解放処理を行うのは面倒




              copyright(c) 2012 kuwata-lab.com all rights reserved.
Solution: at_end()

sub	 fx_file	 {
	 	 my	 ($content)	 =	 (@_);
	 	 $file	 =	 "test-".rand().".txt";
	 	 write_file($file,	 $content);
	 	 at_end	 {	 unlink($file);	 };
	 	 return	 $file;
}
          Register task called at end of test
           テストの終わりに実行される処理を登録する




             copyright(c) 2012 kuwata-lab.com all rights reserved.
Solution: at_end()

spec	 "returns	 length	 of	 file",	 sub	 {
	 	 my	 $file	 =	 fx_file("homhom");
	 	 OK(file_length($file)	 ==	 6);
	 	 unlink	 $file;
};


           No need to teardown for each test
           テストごとの終了処理を書く必要がなくなる




              copyright(c) 2012 kuwata-lab.com all rights reserved.
Solution: at_end()
                                                 https://gist.github.com/3798046

our	 @_CLOSURES	 =	 ();

sub	 at_end(&)	 {
	 	 my	 ($closure)	 =	 @_;
	 	 push	 @_CLOSURES,	 $closure;
}




              copyright(c) 2012 kuwata-lab.com all rights reserved.
Solution: _run_spec()
                                                 https://gist.github.com/3798046

sub	 _run_spec	 {
	 	 my	 ($text,	 $num,	 $closure)	 =	 @_;
	 	 eval	 {	 $closure->();	 };
	 	 my	 $s	 =	 $@	 ?	 "ok"	 :	 "not	 ok";
	 	 print	 "$s	 $num	 -	 $textn";
	 	 my	 $@	 =	 undef;
	 	 for	 my	 $clos	 (reverse(@_CLOSURES))	 {
	 	 	 	 $clos->();
	 	 }
	 	 @_CLOSURES	 =	 ();
}


              copyright(c) 2012 kuwata-lab.com all rights reserved.
Conclusion in this section
✦ No   fixture feature in Test::More
  Test::Moreにはfixture機能がない

✦ SetUp()/tearDown()                    are not so good
  setUp()/tearDown()も良くはない

✦ Use end_at() which registers teardown
 task in setup
  作成時に解放処理を登録できるat_end()が便利




               copyright(c) 2012 kuwata-lab.com all rights reserved.
Section 5:

Hard to distinguish assertions
どれがアサーションなのか分かりにくい




             copyright(c) 2012 kuwata-lab.com all rights reserved.
Assertions in xUnit

Consistent naming rule for assertion methods.
アサーションメソッドに一貫した命名規則がある

   Easy to distinguish assertions in test code
   テストコード中でアサーションを見分けるのが簡単


assertTrue()
assertEqual()
assertMatch()
assertSet()
...


             copyright(c) 2012 kuwata-lab.com all rights reserved.
Assertions in Test::More

No naming rule for assertion functions.
アサーションメソッドに一貫した命名規則がない

   Hard to distinguish assertions in test code
   テストコード中でアサーションを見分けるのが困難


ok()
is()
like()
eq_set()
cmp_ok()
...

             copyright(c) 2012 kuwata-lab.com all rights reserved.
Solution: AssertionObject class
                                                 https://gist.github.com/3798075
Collect assertion functions in a class.
アサーション関数を1つのクラスに集約

package	 AssertionObject;

##	 assertion	 methods
sub	 num_eq	 {	 ...	 }
sub	 str_eq	 {	 ...	 }
sub	 match	 	 {	 ...	 }
...



              copyright(c) 2012 kuwata-lab.com all rights reserved.
Solution: OK()
                                                  https://gist.github.com/3798075
Change OK() which returns AssertionObject.
AssertionObjectインスタンスを返すようにOK()を変更

sub	 OK	 {
	 	 my	 ($actual)	 =	 @_;
	 	 return	 AssertionObject->new($actual);
}
                      Easy to distinguish assertions
##	 usage               どれがアサーションかすぐ分かる
OK	 (1+1)->num_eq(2);
OK	 ("a")->str_eq("a");
OK	 ("9")->match(qr/^d+/);

               copyright(c) 2012 kuwata-lab.com all rights reserved.
Solution: Operator overload
                                                   https://gist.github.com/3798075

package	 AssertionObject;
use	 overload
	 	 	 	 '=='	 =>	 &num_eq,
	 	 	 	 'eq'	 =>	 &str_eq;

package	 main;
OK	 (1+1)	 ==	 2;	 	 	 	 	 //	 same	 as	 num_eq()
OK	 ("a")	 eq	 "a";	 	 	 //	 same	 as	 str_eq()

             Shortcut to assertion methods
             アサーションメソッドを簡単呼び出し


                copyright(c) 2012 kuwata-lab.com all rights reserved.
ok() vs. OK()

ok(2 == 1);
not	 ok	 1
#	 	 	 Failed	 test	 at	 homhom.t	 line	 4.


OK(2) == 1;
not	 ok	 1
#	 	 	 AssertionFailed	 at	 homhom.t	 line	 4.
#	 	 	 $actual	 ==	 $expected:	 failed
#	 	 	 	 	 actual:	 	 	 2
#	 	 	 	 	 expected:	 1

                copyright(c) 2012 kuwata-lab.com all rights reserved.
is() vs. OK()

is() :
is("1.0",	 1.0);	 	 	 	 	 #=>	 not	 ok


OK() :
 OK	 ("1.0")	 eq	 1.0;	 	 	 #=>	 not	 ok
 OK	 ("1.0")	 ==	 1.0;	 	 	 #=>	 ok

              You can choose 'eq' or '=='.
                文字列演算子と数値演算子を選べる


                copyright(c) 2012 kuwata-lab.com all rights reserved.
Test::More vs. OK()

Test::More                                         OK()
ok(1+1	 ==	 2);                                     OK	 (1+1)	 ==	 2;
isnt(1+1,	 0);                                      OK	 (1+1)	 !=	 0;
cmp_ok(1+1,	 '>=',	 1);                             OK	 (1+1)	 >=	 1;



                                             Consistent usage
                                                  一貫した使い方




             copyright(c) 2012 kuwata-lab.com all rights reserved.
Conclusion in this section
✦ Noconsistent naming rule of assertion
 functions in Test::Unit
  Test::Moreにはアサーション関数に一貫した命名規則がない

✦ Hard   to distinguish assertions in test
  テストコード中でどれがアサーションか見分けるのが困難

✦ Solution:
          AssertionObject class and
 operator overload
  解決策:AssertionObjectクラスと演算子オーバーロード




               copyright(c) 2012 kuwata-lab.com all rights reserved.
One More Thing...
Oktest.pm - a new style testing library
               http://search.cpan.org/~kwatch/Oktest/lib/Oktest.pm

use	 strict;
use	 warnings;
no	 warnings	 'void';	 	 #	 RECOMMENDED!
use	 Oktest;
topic	 "ClassName",	 sub	 {
	 	 	 	 topic	 "method_name()",	 sub	 {
	 	 	 	 	 	 	 	 spec	 "...detail...",	 sub	 {
	 	 	 	 	 	 	 	 	 	 	 	 OK	 (1+1)	 ==	 2;
	 	 	 	 	 	 	 	 	 	 	 	 OK	 ('a'	 x	 3)	 eq	 'aaa';
	 	 	 	 	 	 	 	 };
	 	 	 	 };
};
Oktest::main()	 if	 $0	 eq	 __FILE__;
1;
                   copyright(c) 2012 kuwata-lab.com all rights reserved.
おしまい

Contenu connexe

Tendances

Beginning PHPUnit
Beginning PHPUnitBeginning PHPUnit
Beginning PHPUnitJace Ju
 
C ISRO Debugging
C ISRO DebuggingC ISRO Debugging
C ISRO Debuggingsplix757
 
ITT 2014 - Eric Lafortune - ProGuard, Optimizer and Obfuscator in the Android...
ITT 2014 - Eric Lafortune - ProGuard, Optimizer and Obfuscator in the Android...ITT 2014 - Eric Lafortune - ProGuard, Optimizer and Obfuscator in the Android...
ITT 2014 - Eric Lafortune - ProGuard, Optimizer and Obfuscator in the Android...Istanbul Tech Talks
 
The Anatomy of an Exploit (NDC TechTown 2019)
The Anatomy of an Exploit (NDC TechTown 2019)The Anatomy of an Exploit (NDC TechTown 2019)
The Anatomy of an Exploit (NDC TechTown 2019)Patricia Aas
 
Eric Lafortune - ProGuard: Optimizer and obfuscator in the Android SDK
Eric Lafortune - ProGuard: Optimizer and obfuscator in the Android SDKEric Lafortune - ProGuard: Optimizer and obfuscator in the Android SDK
Eric Lafortune - ProGuard: Optimizer and obfuscator in the Android SDKGuardSquare
 
Eric Lafortune - ProGuard and DexGuard for optimization and protection
Eric Lafortune - ProGuard and DexGuard for optimization and protectionEric Lafortune - ProGuard and DexGuard for optimization and protection
Eric Lafortune - ProGuard and DexGuard for optimization and protectionGuardSquare
 
Test-driven Development for TYPO3
Test-driven Development for TYPO3Test-driven Development for TYPO3
Test-driven Development for TYPO3Oliver Klee
 
Architecture for Massively Parallel HDL Simulations
Architecture for Massively Parallel HDL Simulations Architecture for Massively Parallel HDL Simulations
Architecture for Massively Parallel HDL Simulations DVClub
 
Eric Lafortune - The Jack and Jill build system
Eric Lafortune - The Jack and Jill build systemEric Lafortune - The Jack and Jill build system
Eric Lafortune - The Jack and Jill build systemGuardSquare
 
12. stl örnekler
12.  stl örnekler12.  stl örnekler
12. stl örneklerkarmuhtam
 
C++ for Java Developers (JavaZone Academy 2018)
C++ for Java Developers (JavaZone Academy 2018)C++ for Java Developers (JavaZone Academy 2018)
C++ for Java Developers (JavaZone Academy 2018)Patricia Aas
 
The Ring programming language version 1.5.3 book - Part 14 of 184
The Ring programming language version 1.5.3 book - Part 14 of 184The Ring programming language version 1.5.3 book - Part 14 of 184
The Ring programming language version 1.5.3 book - Part 14 of 184Mahmoud Samir Fayed
 
Test-driven JavaScript Development - OPITZ CONSULTING - Tobias Bosch - Stefa...
Test-driven JavaScript Development - OPITZ CONSULTING -  Tobias Bosch - Stefa...Test-driven JavaScript Development - OPITZ CONSULTING -  Tobias Bosch - Stefa...
Test-driven JavaScript Development - OPITZ CONSULTING - Tobias Bosch - Stefa...OPITZ CONSULTING Deutschland
 
20130530-PEGjs
20130530-PEGjs20130530-PEGjs
20130530-PEGjszuqqhi 2
 
JBoss Drools
JBoss DroolsJBoss Drools
JBoss DroolsVictor_Cr
 
Make Testing Groovy
Make Testing GroovyMake Testing Groovy
Make Testing GroovyPaul King
 
Test-Driven Development for TYPO3
Test-Driven Development for TYPO3Test-Driven Development for TYPO3
Test-Driven Development for TYPO3Oliver Klee
 
Phpをいじり倒す10の方法
Phpをいじり倒す10の方法Phpをいじり倒す10の方法
Phpをいじり倒す10の方法Moriyoshi Koizumi
 

Tendances (20)

Beginning PHPUnit
Beginning PHPUnitBeginning PHPUnit
Beginning PHPUnit
 
C&cpu
C&cpuC&cpu
C&cpu
 
C ISRO Debugging
C ISRO DebuggingC ISRO Debugging
C ISRO Debugging
 
ITT 2014 - Eric Lafortune - ProGuard, Optimizer and Obfuscator in the Android...
ITT 2014 - Eric Lafortune - ProGuard, Optimizer and Obfuscator in the Android...ITT 2014 - Eric Lafortune - ProGuard, Optimizer and Obfuscator in the Android...
ITT 2014 - Eric Lafortune - ProGuard, Optimizer and Obfuscator in the Android...
 
The Anatomy of an Exploit (NDC TechTown 2019)
The Anatomy of an Exploit (NDC TechTown 2019)The Anatomy of an Exploit (NDC TechTown 2019)
The Anatomy of an Exploit (NDC TechTown 2019)
 
Eric Lafortune - ProGuard: Optimizer and obfuscator in the Android SDK
Eric Lafortune - ProGuard: Optimizer and obfuscator in the Android SDKEric Lafortune - ProGuard: Optimizer and obfuscator in the Android SDK
Eric Lafortune - ProGuard: Optimizer and obfuscator in the Android SDK
 
Eric Lafortune - ProGuard and DexGuard for optimization and protection
Eric Lafortune - ProGuard and DexGuard for optimization and protectionEric Lafortune - ProGuard and DexGuard for optimization and protection
Eric Lafortune - ProGuard and DexGuard for optimization and protection
 
report
reportreport
report
 
Test-driven Development for TYPO3
Test-driven Development for TYPO3Test-driven Development for TYPO3
Test-driven Development for TYPO3
 
Architecture for Massively Parallel HDL Simulations
Architecture for Massively Parallel HDL Simulations Architecture for Massively Parallel HDL Simulations
Architecture for Massively Parallel HDL Simulations
 
Eric Lafortune - The Jack and Jill build system
Eric Lafortune - The Jack and Jill build systemEric Lafortune - The Jack and Jill build system
Eric Lafortune - The Jack and Jill build system
 
12. stl örnekler
12.  stl örnekler12.  stl örnekler
12. stl örnekler
 
C++ for Java Developers (JavaZone Academy 2018)
C++ for Java Developers (JavaZone Academy 2018)C++ for Java Developers (JavaZone Academy 2018)
C++ for Java Developers (JavaZone Academy 2018)
 
The Ring programming language version 1.5.3 book - Part 14 of 184
The Ring programming language version 1.5.3 book - Part 14 of 184The Ring programming language version 1.5.3 book - Part 14 of 184
The Ring programming language version 1.5.3 book - Part 14 of 184
 
Test-driven JavaScript Development - OPITZ CONSULTING - Tobias Bosch - Stefa...
Test-driven JavaScript Development - OPITZ CONSULTING -  Tobias Bosch - Stefa...Test-driven JavaScript Development - OPITZ CONSULTING -  Tobias Bosch - Stefa...
Test-driven JavaScript Development - OPITZ CONSULTING - Tobias Bosch - Stefa...
 
20130530-PEGjs
20130530-PEGjs20130530-PEGjs
20130530-PEGjs
 
JBoss Drools
JBoss DroolsJBoss Drools
JBoss Drools
 
Make Testing Groovy
Make Testing GroovyMake Testing Groovy
Make Testing Groovy
 
Test-Driven Development for TYPO3
Test-Driven Development for TYPO3Test-Driven Development for TYPO3
Test-Driven Development for TYPO3
 
Phpをいじり倒す10の方法
Phpをいじり倒す10の方法Phpをいじり倒す10の方法
Phpをいじり倒す10の方法
 

Similaire à What is wrong on Test::More? / Test::Moreが抱える問題点とその解決策

Advanced Perl Techniques
Advanced Perl TechniquesAdvanced Perl Techniques
Advanced Perl TechniquesDave Cross
 
Testing Code and Assuring Quality
Testing Code and Assuring QualityTesting Code and Assuring Quality
Testing Code and Assuring QualityKent Cowgill
 
Intro to Testing in Zope, Plone
Intro to Testing in Zope, PloneIntro to Testing in Zope, Plone
Intro to Testing in Zope, PloneQuintagroup
 
using python module: doctest
using python module: doctestusing python module: doctest
using python module: doctestmitnk
 
Practical Testing of Ruby Core
Practical Testing of Ruby CorePractical Testing of Ruby Core
Practical Testing of Ruby CoreHiroshi SHIBATA
 
Cより速いRubyプログラム
Cより速いRubyプログラムCより速いRubyプログラム
Cより速いRubyプログラムkwatch
 
Introducing PHP Latest Updates
Introducing PHP Latest UpdatesIntroducing PHP Latest Updates
Introducing PHP Latest UpdatesIftekhar Eather
 
Groovy Introduction - JAX Germany - 2008
Groovy Introduction - JAX Germany - 2008Groovy Introduction - JAX Germany - 2008
Groovy Introduction - JAX Germany - 2008Guillaume Laforge
 
Go 1.10 Release Party - PDX Go
Go 1.10 Release Party - PDX GoGo 1.10 Release Party - PDX Go
Go 1.10 Release Party - PDX GoRodolfo Carvalho
 
A Lifecycle Of Code Under Test by Robert Fornal
A Lifecycle Of Code Under Test by Robert FornalA Lifecycle Of Code Under Test by Robert Fornal
A Lifecycle Of Code Under Test by Robert FornalQA or the Highway
 
Spock: Test Well and Prosper
Spock: Test Well and ProsperSpock: Test Well and Prosper
Spock: Test Well and ProsperKen Kousen
 
Why you should be using the shiny new C# 6.0 features now!
Why you should be using the shiny new C# 6.0 features now!Why you should be using the shiny new C# 6.0 features now!
Why you should be using the shiny new C# 6.0 features now!Eric Phan
 
PHPunit and you
PHPunit and youPHPunit and you
PHPunit and youmarkstory
 
The $path to knowledge: What little it take to unit-test Perl.
The $path to knowledge: What little it take to unit-test Perl.The $path to knowledge: What little it take to unit-test Perl.
The $path to knowledge: What little it take to unit-test Perl.Workhorse Computing
 
O CPAN tem as ferramentas que você precisa para fazer TDD em Perl, o Coding D...
O CPAN tem as ferramentas que você precisa para fazer TDD em Perl, o Coding D...O CPAN tem as ferramentas que você precisa para fazer TDD em Perl, o Coding D...
O CPAN tem as ferramentas que você precisa para fazer TDD em Perl, o Coding D...Rodolfo Carvalho
 
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
Unit testing in iOS featuring OCUnit, GHUnit & OCMockUnit testing in iOS featuring OCUnit, GHUnit & OCMock
Unit testing in iOS featuring OCUnit, GHUnit & OCMockRobot Media
 

Similaire à What is wrong on Test::More? / Test::Moreが抱える問題点とその解決策 (20)

Advanced Perl Techniques
Advanced Perl TechniquesAdvanced Perl Techniques
Advanced Perl Techniques
 
Testing Code and Assuring Quality
Testing Code and Assuring QualityTesting Code and Assuring Quality
Testing Code and Assuring Quality
 
Intro to Testing in Zope, Plone
Intro to Testing in Zope, PloneIntro to Testing in Zope, Plone
Intro to Testing in Zope, Plone
 
using python module: doctest
using python module: doctestusing python module: doctest
using python module: doctest
 
Test driven development_for_php
Test driven development_for_phpTest driven development_for_php
Test driven development_for_php
 
Practical Testing of Ruby Core
Practical Testing of Ruby CorePractical Testing of Ruby Core
Practical Testing of Ruby Core
 
Cより速いRubyプログラム
Cより速いRubyプログラムCより速いRubyプログラム
Cより速いRubyプログラム
 
Introducing PHP Latest Updates
Introducing PHP Latest UpdatesIntroducing PHP Latest Updates
Introducing PHP Latest Updates
 
Groovy Introduction - JAX Germany - 2008
Groovy Introduction - JAX Germany - 2008Groovy Introduction - JAX Germany - 2008
Groovy Introduction - JAX Germany - 2008
 
Go 1.10 Release Party - PDX Go
Go 1.10 Release Party - PDX GoGo 1.10 Release Party - PDX Go
Go 1.10 Release Party - PDX Go
 
A Lifecycle Of Code Under Test by Robert Fornal
A Lifecycle Of Code Under Test by Robert FornalA Lifecycle Of Code Under Test by Robert Fornal
A Lifecycle Of Code Under Test by Robert Fornal
 
Specs2
Specs2Specs2
Specs2
 
Spock: Test Well and Prosper
Spock: Test Well and ProsperSpock: Test Well and Prosper
Spock: Test Well and Prosper
 
Why you should be using the shiny new C# 6.0 features now!
Why you should be using the shiny new C# 6.0 features now!Why you should be using the shiny new C# 6.0 features now!
Why you should be using the shiny new C# 6.0 features now!
 
PHPunit and you
PHPunit and youPHPunit and you
PHPunit and you
 
PHPSpec BDD for PHP
PHPSpec BDD for PHPPHPSpec BDD for PHP
PHPSpec BDD for PHP
 
The $path to knowledge: What little it take to unit-test Perl.
The $path to knowledge: What little it take to unit-test Perl.The $path to knowledge: What little it take to unit-test Perl.
The $path to knowledge: What little it take to unit-test Perl.
 
O CPAN tem as ferramentas que você precisa para fazer TDD em Perl, o Coding D...
O CPAN tem as ferramentas que você precisa para fazer TDD em Perl, o Coding D...O CPAN tem as ferramentas que você precisa para fazer TDD em Perl, o Coding D...
O CPAN tem as ferramentas que você precisa para fazer TDD em Perl, o Coding D...
 
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
Unit testing in iOS featuring OCUnit, GHUnit & OCMockUnit testing in iOS featuring OCUnit, GHUnit & OCMock
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
 
Hidden Gems of Ruby 1.9
Hidden Gems of Ruby 1.9Hidden Gems of Ruby 1.9
Hidden Gems of Ruby 1.9
 

Plus de kwatch

How to make the fastest Router in Python
How to make the fastest Router in PythonHow to make the fastest Router in Python
How to make the fastest Router in Pythonkwatch
 
Migr8.rb チュートリアル
Migr8.rb チュートリアルMigr8.rb チュートリアル
Migr8.rb チュートリアルkwatch
 
なんでもID
なんでもIDなんでもID
なんでもIDkwatch
 
Nippondanji氏に怒られても仕方ない、配列型とJSON型の使い方
Nippondanji氏に怒られても仕方ない、配列型とJSON型の使い方Nippondanji氏に怒られても仕方ない、配列型とJSON型の使い方
Nippondanji氏に怒られても仕方ない、配列型とJSON型の使い方kwatch
 
【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方
【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方
【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方kwatch
 
O/Rマッパーによるトラブルを未然に防ぐ
O/Rマッパーによるトラブルを未然に防ぐO/Rマッパーによるトラブルを未然に防ぐ
O/Rマッパーによるトラブルを未然に防ぐkwatch
 
正規表現リテラルは本当に必要なのか?
正規表現リテラルは本当に必要なのか?正規表現リテラルは本当に必要なのか?
正規表現リテラルは本当に必要なのか?kwatch
 
【公開終了】Python4PHPer - PHPユーザのためのPython入門 (Python2.5)
【公開終了】Python4PHPer - PHPユーザのためのPython入門 (Python2.5)【公開終了】Python4PHPer - PHPユーザのためのPython入門 (Python2.5)
【公開終了】Python4PHPer - PHPユーザのためのPython入門 (Python2.5)kwatch
 
DBスキーマもバージョン管理したい!
DBスキーマもバージョン管理したい!DBスキーマもバージョン管理したい!
DBスキーマもバージョン管理したい!kwatch
 
PHPとJavaScriptにおけるオブジェクト指向を比較する
PHPとJavaScriptにおけるオブジェクト指向を比較するPHPとJavaScriptにおけるオブジェクト指向を比較する
PHPとJavaScriptにおけるオブジェクト指向を比較するkwatch
 
SQL上級者こそ知って欲しい、なぜO/Rマッパーが重要か?
SQL上級者こそ知って欲しい、なぜO/Rマッパーが重要か?SQL上級者こそ知って欲しい、なぜO/Rマッパーが重要か?
SQL上級者こそ知って欲しい、なぜO/Rマッパーが重要か?kwatch
 
Fantastic DSL in Python
Fantastic DSL in PythonFantastic DSL in Python
Fantastic DSL in Pythonkwatch
 
PHP5.5新機能「ジェネレータ」初心者入門
PHP5.5新機能「ジェネレータ」初心者入門PHP5.5新機能「ジェネレータ」初心者入門
PHP5.5新機能「ジェネレータ」初心者入門kwatch
 
Pretty Good Branch Strategy for Git/Mercurial
Pretty Good Branch Strategy for Git/MercurialPretty Good Branch Strategy for Git/Mercurial
Pretty Good Branch Strategy for Git/Mercurialkwatch
 
Oktest - a new style testing library for Python -
Oktest - a new style testing library for Python -Oktest - a new style testing library for Python -
Oktest - a new style testing library for Python -kwatch
 
文字列結合のベンチマークをいろんな処理系でやってみた
文字列結合のベンチマークをいろんな処理系でやってみた文字列結合のベンチマークをいろんな処理系でやってみた
文字列結合のベンチマークをいろんな処理系でやってみたkwatch
 
I have something to say about the buzz word "From Java to Ruby"
I have something to say about the buzz word "From Java to Ruby"I have something to say about the buzz word "From Java to Ruby"
I have something to say about the buzz word "From Java to Ruby"kwatch
 
Javaより速いLL用テンプレートエンジン
Javaより速いLL用テンプレートエンジンJavaより速いLL用テンプレートエンジン
Javaより速いLL用テンプレートエンジンkwatch
 
Underlaying Technology of Modern O/R Mapper
Underlaying Technology of Modern O/R MapperUnderlaying Technology of Modern O/R Mapper
Underlaying Technology of Modern O/R Mapperkwatch
 
How to Make Ruby CGI Script Faster - CGIを高速化する小手先テクニック -
How to Make Ruby CGI Script Faster - CGIを高速化する小手先テクニック -How to Make Ruby CGI Script Faster - CGIを高速化する小手先テクニック -
How to Make Ruby CGI Script Faster - CGIを高速化する小手先テクニック -kwatch
 

Plus de kwatch (20)

How to make the fastest Router in Python
How to make the fastest Router in PythonHow to make the fastest Router in Python
How to make the fastest Router in Python
 
Migr8.rb チュートリアル
Migr8.rb チュートリアルMigr8.rb チュートリアル
Migr8.rb チュートリアル
 
なんでもID
なんでもIDなんでもID
なんでもID
 
Nippondanji氏に怒られても仕方ない、配列型とJSON型の使い方
Nippondanji氏に怒られても仕方ない、配列型とJSON型の使い方Nippondanji氏に怒られても仕方ない、配列型とJSON型の使い方
Nippondanji氏に怒られても仕方ない、配列型とJSON型の使い方
 
【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方
【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方
【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方
 
O/Rマッパーによるトラブルを未然に防ぐ
O/Rマッパーによるトラブルを未然に防ぐO/Rマッパーによるトラブルを未然に防ぐ
O/Rマッパーによるトラブルを未然に防ぐ
 
正規表現リテラルは本当に必要なのか?
正規表現リテラルは本当に必要なのか?正規表現リテラルは本当に必要なのか?
正規表現リテラルは本当に必要なのか?
 
【公開終了】Python4PHPer - PHPユーザのためのPython入門 (Python2.5)
【公開終了】Python4PHPer - PHPユーザのためのPython入門 (Python2.5)【公開終了】Python4PHPer - PHPユーザのためのPython入門 (Python2.5)
【公開終了】Python4PHPer - PHPユーザのためのPython入門 (Python2.5)
 
DBスキーマもバージョン管理したい!
DBスキーマもバージョン管理したい!DBスキーマもバージョン管理したい!
DBスキーマもバージョン管理したい!
 
PHPとJavaScriptにおけるオブジェクト指向を比較する
PHPとJavaScriptにおけるオブジェクト指向を比較するPHPとJavaScriptにおけるオブジェクト指向を比較する
PHPとJavaScriptにおけるオブジェクト指向を比較する
 
SQL上級者こそ知って欲しい、なぜO/Rマッパーが重要か?
SQL上級者こそ知って欲しい、なぜO/Rマッパーが重要か?SQL上級者こそ知って欲しい、なぜO/Rマッパーが重要か?
SQL上級者こそ知って欲しい、なぜO/Rマッパーが重要か?
 
Fantastic DSL in Python
Fantastic DSL in PythonFantastic DSL in Python
Fantastic DSL in Python
 
PHP5.5新機能「ジェネレータ」初心者入門
PHP5.5新機能「ジェネレータ」初心者入門PHP5.5新機能「ジェネレータ」初心者入門
PHP5.5新機能「ジェネレータ」初心者入門
 
Pretty Good Branch Strategy for Git/Mercurial
Pretty Good Branch Strategy for Git/MercurialPretty Good Branch Strategy for Git/Mercurial
Pretty Good Branch Strategy for Git/Mercurial
 
Oktest - a new style testing library for Python -
Oktest - a new style testing library for Python -Oktest - a new style testing library for Python -
Oktest - a new style testing library for Python -
 
文字列結合のベンチマークをいろんな処理系でやってみた
文字列結合のベンチマークをいろんな処理系でやってみた文字列結合のベンチマークをいろんな処理系でやってみた
文字列結合のベンチマークをいろんな処理系でやってみた
 
I have something to say about the buzz word "From Java to Ruby"
I have something to say about the buzz word "From Java to Ruby"I have something to say about the buzz word "From Java to Ruby"
I have something to say about the buzz word "From Java to Ruby"
 
Javaより速いLL用テンプレートエンジン
Javaより速いLL用テンプレートエンジンJavaより速いLL用テンプレートエンジン
Javaより速いLL用テンプレートエンジン
 
Underlaying Technology of Modern O/R Mapper
Underlaying Technology of Modern O/R MapperUnderlaying Technology of Modern O/R Mapper
Underlaying Technology of Modern O/R Mapper
 
How to Make Ruby CGI Script Faster - CGIを高速化する小手先テクニック -
How to Make Ruby CGI Script Faster - CGIを高速化する小手先テクニック -How to Make Ruby CGI Script Faster - CGIを高速化する小手先テクニック -
How to Make Ruby CGI Script Faster - CGIを高速化する小手先テクニック -
 

Dernier

"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr BaganFwdays
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Manik S Magar
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024The Digital Insurer
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsMemoori
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyAlfredo García Lavilla
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machinePadma Pradeep
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticscarlostorres15106
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxhariprasad279825
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
Story boards and shot lists for my a level piece
Story boards and shot lists for my a level pieceStory boards and shot lists for my a level piece
Story boards and shot lists for my a level piececharlottematthew16
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 

Dernier (20)

"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!
 
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptxE-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial Buildings
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easy
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machine
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptx
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
Story boards and shot lists for my a level piece
Story boards and shot lists for my a level pieceStory boards and shot lists for my a level piece
Story boards and shot lists for my a level piece
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 

What is wrong on Test::More? / Test::Moreが抱える問題点とその解決策

  • 1. YAPC::ASIA 2012 What is wrong on Test::More? Test::Moreが抱える問題点とその解決策 makoto kuwata <kwa@kuwata-lab.com> http://www.kuwata-lab.com/ 2012-09-27 (Fri) copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 2. Agenda ✦ Testing without spec 仕様を書かずにテストしてる ✦ Not structured tests テストが構造化されてない ✦ Needs test plan 事前にテストプラン (=テスト数) を必要とする ✦ No fixture feature フィクスチャ機能がない ✦ Hard to distinguish assertions どれがアサーションなのか分かりにくい copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 3. Section 1: Testing without spec 仕様を書かずにテストしている copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 4. Point Write your test according to spec, not your code. テストは、コードではなく仕様をもとに書け。 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 5. Sample: Test::More (Perl) use Test::More tests => 4; is f(0), 0; is f(1), 1; is f(2), 1; is f(3), 2; copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 6. Sample: RSpec (Ruby) describe 'f()' it "calculates fibonacchi sequence" do f(0).should == 0 f(1).should == 1 f(2).should == 1 f(3).should == 2 end end copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 7. Sample: unittest (Python) import unittest class FooTest(unittest.TestCase): def test_calculates_fiboacchi_seq(self): """Calculates Fibonacchi sequence""" self.assertEqual(0, f(0)) self.assertEqual(1, f(1)) self.assertEqual(1, f(2)) self.assertEqual(2, f(3)) copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 8. Goal of test ✦ Test::More: Does that code run correctly? そのコードは意図した通りに動くか? ✦ RSpec: Does that code satisfy the spec? そのコードは仕様を満たしているか? copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 9. Difference between Test::More and RSpec ## Test::More like $html, qr'<h3>Hello</h3>'; Higher-level information より高水準の情報 ## RSpec it "contains section title" do html.should =~ %r'<h3>Hello</h3>' end copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 10. Solution: spec() https://gist.github.com/3797929 sub spec { my ($text, $block) = @_; $block->(); } ## usage spec "page contains section title", sub { ok(render() =~ qr`<h1>Hello</h1>`); }; copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 11. Spec First Step 1. Write specifications spec "...specification1..."; spec "...specification2..."; #=> not ok 1 - ...spec... # TODO #=> not ok 2 - ...spec... # TODO Step 2. Add assertions according to spec spec "...specification...", sub { assertion1; assertion2; }; copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 12. Solution: spec() https://gist.github.com/3797939 sub spec { my ($text, $block) = @_; return $block->() if $block; TODO: { local $TODO = ": not implemented yet"; ok(undef); } } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 13. Meaning of output lines As is - 'ok' when assertion passed, 'not ok' when failed アサーションが成功したらok、失敗したらnot ok ok 1 - assertion1 not ok 2 - assertion2 To be - 'ok' when spec satisfied, 'not ok' when not コードが仕様を満たしたらok、満たさなければnot ok ok 1 - specification1 not ok 2 - specification2 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 14. Spec : Assertion = 1 : N ✦A specification can contain some assertions 1つの仕様に複数のアサーションを書いてよい spec "returns pair of integer", sub { is scalar(@$ret), 2; like $ret->[0], qr/^d+$/; like $ret->[1], qr/^d+$/; }; copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 15. Spec : Assertion = 1 : N As is Output lines per assertion アサーションごとに出力行 ok 1 ok 2 ok 3 To be ok 1 - returns pair of integer Output lines per spec 仕様ごとに出力行 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 16. Solution: OK() https://gist.github.com/3797954 sub OK { my ($expr) = @_; unless ($expr) { my ($pkg, $file, $lineno) = caller(); die "AssertionFailed" ." at $file line $lineno.n"; } } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 17. Solution: spec() https://gist.github.com/3797954 my $spec = undef; my $num = 0; sub spec { my ($text, $block) = @_; $spec = $text; $num++; eval { $block->(); }; my $err = $@; if (! $err) { print "ok $num - $textn"; } else { print "not ok $num - $textn"; $err =~ s/^/# /mg; $err .= "n" if $err !~ /nz/; print STDERR $err; } } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 18. Conclusion of this section ✦ Write test based on spec, not on code テストは、コードに対してではなく、仕様に対して書く ✦ 'Spec specified?' instead of 'Run correctly?' 「正しく動作するか?」ではなく「仕様を満たしているか?」 ✦ Spec first, assertion second 仕様を先に書いて、そのあとにアサーションを書く ✦ Spec : Assertion = 1 : N 1つの仕様が複数のアサーションを含んでよい ✦ Output line per spec, not assertion 出力行のok / not okはアサーション単位ではなく仕様単位に出す copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 19. Section 2: Not structured tests テストが構造化されてない copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 20. Point Test should have structure. Because spec has structure. テストには構造がある。なぜなら仕様に構造があるから。 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 21. Sample: Specification document クラス:Calendar メソッド:isLeapYear(int year): boolean 動作詳細: ・100で割り切れる場合、 ・400で割り切れる場合はtrueを返す ・それ以外はfalseを返す ・4で割り切れる場合はtrueを返す ・それ以外はfalseを返す copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 22. Test code as spec document Is your test code available as spec document? そのコードは仕様書としてほんとに利用できるの? copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 23. Sample: Test::More use Test::More tests => 2; use Foo; $foo = Foo->new(); is(Foo->new()->meth1(), 11); is(Foo->new()->meth2(), 12); copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 24. Sample: RSpec Test target (class, method, ...) require 'rspec' テスト対象 (クラス、メソッド、…) describe Foo do describe '#bar()' do context 'when arg is provided' do it "returns length" do Foo.new.methd1([0,1,2]).should == 3 Foo.new.methd1([]).should == 0 end end end end copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 25. Sample: RSpec Test condition or situation require 'rspec' 条件や状況 describe Foo do describe '#bar()' do context 'when arg is provided' do it "returns length" do Foo.new.methd1([0,1,2]).should == 3 Foo.new.methd1([]).should == 0 end end end end copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 26. Sample: RSpec require 'rspec' describe Foo do describe '#bar()' do context 'when arg is provided' do it "returns length" do Foo.new.methd1([0,1,2]).should == 3 Foo.new.methd1([]).should == 0 end end end Specification 仕様 end copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 27. Sample: unittest (Python) import unittest class FooTest(unitteset.TestCase): def test_bar_1(self): """returns length of arg passed""" self.assertequal(3, Foo().bar([1,2,3])) def test_bar_2(self): """returns 0 when arg is not passed""" self.assertEqual(0, Foo().bar()) copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 28. Sample: Test::Unit2 (Ruby) require 'test/unit' require 'foo' class FooTest < Test::Unit::TestCase class MethTest < self def test_returns_length_of_arg n = Foo.new.bar([1,2,3]) assert_equal 3, n end end end ref: http://www.clear-code.com/blog/2012/4/25.html copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 29. Sample: subtest() (Test::More) use Test::More tests=>1; subtest "package Foo", sub { plan tests=>1; subtest "sub bar()", sub { plan tests=>2; ok (1+1 == 2); ok (1-1 == 0); }; }; copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 30. Sample: subtest() (Test::More) $ perl homhom.t 1..1 1..1 1..2 ok 1 ok 2 ok 1 - sub bar() ok 1 - package Foo copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 31. Sample: subtest() (Test::More) $ perl homhom.t 1..1 1..1 1..2 ok 1 ok 2 ok 1 - sub bar() ok 1 - package Foo copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 32. Sample: subtest() (Test::More) $ perl homhom.t 1..1 1..1 1..2 ok 1 ok 2 ok 1 - sub bar() ok 1 - package Foo copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 33. Solution: subtest() alternatives Test target テスト対象 print "1..2n"; topic 'package Foo', sub { topic 'sub meth1()', sub { case_when 'arg is passed', sub { spec "1+1 should be 2", sub { OK(1+1 == 2); }; spec "1-1 should be 0", sub { OK(1-1 == 0); }; }; Condition or situation }; 条件や状況 }; copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 34. Solution: subtest() alternatives $ perl homhom.t 1..2 # * package Foo # * sub meth1() # - when arg is passed ok 1 - 1+1 should be 2 ok 2 - 1-1 should be 0 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 35. Solution: subtest() alternatives https://gist.github.com/3797976 my $depth = 0; sub topic { my ($name, $block) = @_; my $indent = ' ' x $depth; print "# $indent* $namen"; $depth++; $block->(); $depth--; } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 36. Solution: subtest() alternatives https://gist.github.com/3797976 my $depth = 0; sub case_when { my ($condition, $block) = @_; my $indent = ' ' x $depth; print "# $indent- $conditionn"; $depth++; $block->(); $depth--; } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 37. Conclution of this section ✦ Testhas structure, because spec has structure. テストには構造がある。なぜなら仕様に構造があるから。 ✦ xUnit focuses on test automation, RSpec focuses on test structure. xUnitは自動化のための道具、RSpecは構造化のための道具 ✦ Output of subtest() suck. Define your own subtest alternatives. subtest()は出力が残念すぎる。自前関数お勧め。 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 38. Section 3: Needs test plan 事前にテストプラン (=テスト数) を必要とする copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 39. Sample: Test::More use Test::More tests=>2; is(1+1, 2); is(1-1, 0); $ perl homhom.t 1..2 ok 1 ok 2 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 40. Sample: subtest() use Test::More tests=>1; subtest 'package Foo', sub { plan tests=>1; subtest 'sub meth()', sub { plan test2=>1; is(1+1, 2); is(1-1, 0); }; }; copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 41. Pros of test plan ✦ Detect unexpected test finishing テストの異常終了が検知できる • Compare number of ('ok' + 'not ok') with test plan 出力されたokやnot okの数と、 宣言されたテスト個数とを比較 ✦ Necessary to report test progress テスト実行時に進行状況を知るのに必要 • Especially for 'prove' command 特にproveコマンドで copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 42. Cons of test plan ✦ Too messy to keep currect value 正しい値に更新し続けるのが面倒すぎる • Update test plan when you add assertions assertion関数を追加したら忘れずに更新 • back to top when you add at the end of file ファイルの末尾にテストを追加したら、先頭に戻ってテスト数 を更新 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 43. Sample: done_testing() use Test::More tests=>2; is(1+1, 2); is(1-1, 0); done_testing(); $ perl homhom.t ok 1 ok 2 1..2 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 44. done_testing() and subtest() use Test::More tests=>1; subtest "package Foo", sub { subtest "sub meth1()", sub { ok (1+1 == 2); ok (1-1 == 0); done_testing(); }; done_testing(); }; done_testing(); (No need to call done_testing() in subtest() since Test::More 0.95_01) copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 45. Solution: subtest() alternatives $ perl homhom.pl ok 1 ok 2 1..2 ok 1 - sub meth1() 1..1 ok 1 - package Foo 1..1 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 46. Pros of done_testing() ✦ No need to speicfy test plan! テスト数を指定しなくていい! copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 47. Cons of done_testing() ✦ Need to expand TAP spec TAP仕様に拡張が必要 • Simplicity of TAP has gone もはやTAPの簡易性は損なわれた ✦ 'Prove' prints '?' as number of tests proveコマンドで全体のテスト数が「?」に • Degeneration of interface インターフェースとしては退化 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 48. Off Topic: Doubt about TAP ✦ If TAP accepts test plan after running tests, テスト数がわかるのがテスト終了後でいいなら • 'End of test' indicatior is necessary for TAP, but test plan is not, is it? テストの終わりが分かる何かがあればテスト数いらなくね? • Index number of test is not necessary, is it? そもそも ok や not ok に通し番号いらなくね? ok 1 # ..spec.. ok 2 # ..spec.. <<TEST END>> copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 49. The root cause of problem ✦ Impossibleto count number of tests before running tests テスト数を事前に数えられない ✦ To be あるべき姿 • Step1. Count and print number of tests テスト数を数えて出力 • Step2. Run tests テストを実行 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 50. Solution: Intermediate data structure As is: is 1+1, 2; ok 1 is 1-1, 0; ok 2 1..2 To be: topic is 1+1, 2; topic 1..2 is 1-1, 0; ok 1 spec ok 2 spec Easy to count tests copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 51. Cons of intermediate data structure ✦ Easyto count and filter tests before running tests テスト実行前にテストを数えたりフィルタするのが簡単にできる ✦ No need to extend TAP specification TAPの仕様を拡張しなくてよい • No more done_testing() done_testing()なんていらない • No more nesting like subtest() subtest()のような入れ子対応はいらない copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 52. Sample: xUnit class FooTest(TestCase): def test1(self): ... def test2(self): ... ## build intermediate data structure suite = TestSuite() suite.add(FooTest('test1')) suite.add(FooTest('test2')) ## run tests TestRunner().run(suite) copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 53. Solution: topic() and spec() https://gist.github.com/3798000 topic 'class Foo', sub { topic 'sub meth1()', sub { case_when "arg is given", sub { spec "1+1 should be 2", sub { OK(1+1 == 2); }; }; Change to build tree }; }; Traverse tree run_all(); copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 54. Solution: topic() https://gist.github.com/3798000 my $NODES = []; sub topic { my ($name, $block) = @_; my $node = {name=>$name, prefix=>'*', children=>[]}; push @$NODES, $node; my $bkup = $NODES; $NODES = $node->{children}; $block->(); $NODES = $bkup; } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 55. Solution: spec() https://gist.github.com/3798000 my $NODES = []; sub spec { my ($text, $block) = @_; push @$NODES, [$text, $block]; } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 56. Solution: _count_specs() https://gist.github.com/3798000 sub _count_specs { my ($nodes) = @_; my $n = 0; for (@$nodes) { if (ref($_) eq 'HASH') { # topic $n += _count_specs($_->{children}); } elsif (ref($_) eq 'ARRAY') { # spec $n += 1; } } return $n; } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 57. Solution: run_all() https://gist.github.com/3798000 sub run_all { print "1..", _count_specs($NODES), "n"; _run_all($NODES, 0, 0); } sub _run_all { my ($nodes, $depth, $num) = @_; my $indent = ' ' x $depth; for my $x (@$nodes) { if (ref($x) eq 'HASH') { # topic print "# $indent$x->{prefix} $x->{name}n"; $num = _run_all($x->{children}, $depth + 1, $num); } elsif (ref($x) eq 'ARRAY') { # spec my ($text, $block) = @$x; $num++; _run_spec($text, $num, $block); } } return $num; } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 58. Conclustion in this seciton ✦ Don't count tests manually. Use computer. テスト数を手動で数えるのはやめてコンピュータにさせよう ✦ Noneed to expand TAP specification. Both done_testing() and subtest() are wrong. TAPの仕様拡張は不必要。done_testing()もsubtest()も間違い ✦ Intermediate data structure solves the problem. テストを表す中間データ構造を作れば万事解決 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 59. Section 4: No fixture feature フィクスチャ機能がない copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 60. What is fixture? "A test fixture (also known as a test context) is the set of preconditions or state needed to run a test. The developer should set up a known good state before the tests, and return to the original state after the tests." http://en.wikipedia.org/wiki/XUnit "テストを実行、成功させるために必要な状態や前提条件の集合 を、フィクスチャと呼ぶ。これらはテストコンテキストとも呼 ばれる。開発者はテストの実行前にテストに適した状態を整 え、テスト実行後に元の状態を復元することが望ましい。" http://ja.wikipedia.org/wiki/XUnit copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 61. Fixture method ✦ xUnit • setUp() / tearDown() ✦ RSpec • before() / after() ✦ Test::More • (nothing!) copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 62. Fault of setUp/tearDown All tests in a class must share a setUp/tearDown. sub setUp { my ($self) = @_; $self->man = User->new(gender=>'M'); $self->woman = User->new(gender=>'W'); } sub test1 { my ($self) = @_; my $user = $self->man; ... } sub test2 { my ($self) = @_; my $user = $self->woman; ... } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 63. Fault of setUp/tearDown Separate TestCase class? package FooTestCase; sub setUp { ... } sub testFoo { ... } package BarTestCase; sub setUp { ... } sub testBar { ... } No. Test structure should follow specification reason, not fixture reason. copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 64. Another approach on fixture Define fixture method for each test data. sub fx_man { return User->new(gender=>'M'); } sub fx_woman { return User->new(gender=>'W'); } spec "test for man", sub { OK(fx_man()->{gender} eq 'M'); } spec "test for woman", sub { OK(fx_woman()->{gender} eq 'W'); } More flexible than setUp/tearDown copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 65. Cons of the approach spec "returns length of file", sub { my $file = fx_file("homhom"); OK(file_length($file) == 6); unlink($file); }; Troublesome task to do teardown for each fixture data fixtureごとに忘れずに解放処理を行うのは面倒 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 66. Solution: at_end() sub fx_file { my ($content) = (@_); $file = "test-".rand().".txt"; write_file($file, $content); at_end { unlink($file); }; return $file; } Register task called at end of test テストの終わりに実行される処理を登録する copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 67. Solution: at_end() spec "returns length of file", sub { my $file = fx_file("homhom"); OK(file_length($file) == 6); unlink $file; }; No need to teardown for each test テストごとの終了処理を書く必要がなくなる copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 68. Solution: at_end() https://gist.github.com/3798046 our @_CLOSURES = (); sub at_end(&) { my ($closure) = @_; push @_CLOSURES, $closure; } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 69. Solution: _run_spec() https://gist.github.com/3798046 sub _run_spec { my ($text, $num, $closure) = @_; eval { $closure->(); }; my $s = $@ ? "ok" : "not ok"; print "$s $num - $textn"; my $@ = undef; for my $clos (reverse(@_CLOSURES)) { $clos->(); } @_CLOSURES = (); } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 70. Conclusion in this section ✦ No fixture feature in Test::More Test::Moreにはfixture機能がない ✦ SetUp()/tearDown() are not so good setUp()/tearDown()も良くはない ✦ Use end_at() which registers teardown task in setup 作成時に解放処理を登録できるat_end()が便利 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 71. Section 5: Hard to distinguish assertions どれがアサーションなのか分かりにくい copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 72. Assertions in xUnit Consistent naming rule for assertion methods. アサーションメソッドに一貫した命名規則がある Easy to distinguish assertions in test code テストコード中でアサーションを見分けるのが簡単 assertTrue() assertEqual() assertMatch() assertSet() ... copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 73. Assertions in Test::More No naming rule for assertion functions. アサーションメソッドに一貫した命名規則がない Hard to distinguish assertions in test code テストコード中でアサーションを見分けるのが困難 ok() is() like() eq_set() cmp_ok() ... copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 74. Solution: AssertionObject class https://gist.github.com/3798075 Collect assertion functions in a class. アサーション関数を1つのクラスに集約 package AssertionObject; ## assertion methods sub num_eq { ... } sub str_eq { ... } sub match { ... } ... copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 75. Solution: OK() https://gist.github.com/3798075 Change OK() which returns AssertionObject. AssertionObjectインスタンスを返すようにOK()を変更 sub OK { my ($actual) = @_; return AssertionObject->new($actual); } Easy to distinguish assertions ## usage どれがアサーションかすぐ分かる OK (1+1)->num_eq(2); OK ("a")->str_eq("a"); OK ("9")->match(qr/^d+/); copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 76. Solution: Operator overload https://gist.github.com/3798075 package AssertionObject; use overload '==' => &num_eq, 'eq' => &str_eq; package main; OK (1+1) == 2; // same as num_eq() OK ("a") eq "a"; // same as str_eq() Shortcut to assertion methods アサーションメソッドを簡単呼び出し copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 77. ok() vs. OK() ok(2 == 1); not ok 1 # Failed test at homhom.t line 4. OK(2) == 1; not ok 1 # AssertionFailed at homhom.t line 4. # $actual == $expected: failed # actual: 2 # expected: 1 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 78. is() vs. OK() is() : is("1.0", 1.0); #=> not ok OK() : OK ("1.0") eq 1.0; #=> not ok OK ("1.0") == 1.0; #=> ok You can choose 'eq' or '=='. 文字列演算子と数値演算子を選べる copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 79. Test::More vs. OK() Test::More OK() ok(1+1 == 2); OK (1+1) == 2; isnt(1+1, 0); OK (1+1) != 0; cmp_ok(1+1, '>=', 1); OK (1+1) >= 1; Consistent usage 一貫した使い方 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 80. Conclusion in this section ✦ Noconsistent naming rule of assertion functions in Test::Unit Test::Moreにはアサーション関数に一貫した命名規則がない ✦ Hard to distinguish assertions in test テストコード中でどれがアサーションか見分けるのが困難 ✦ Solution: AssertionObject class and operator overload 解決策:AssertionObjectクラスと演算子オーバーロード copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 82. Oktest.pm - a new style testing library http://search.cpan.org/~kwatch/Oktest/lib/Oktest.pm use strict; use warnings; no warnings 'void'; # RECOMMENDED! use Oktest; topic "ClassName", sub { topic "method_name()", sub { spec "...detail...", sub { OK (1+1) == 2; OK ('a' x 3) eq 'aaa'; }; }; }; Oktest::main() if $0 eq __FILE__; 1; copyright(c) 2012 kuwata-lab.com all rights reserved.