ruby初級者向けレッスン 第46回 ─── test::unit

50
2013 6 15 Ruby 初級者向けレッスン 46 — Test::Unit — ひがき @ Ruby 関西

Upload: higaki

Post on 01-Dec-2014

2.200 views

Category:

Technology


2 download

DESCRIPTION

 

TRANSCRIPT

Page 1: Ruby初級者向けレッスン 第46回 ─── Test::Unit

2013年6月15日

Ruby初級者向けレッスン 46回— Test::Unit —

ひがき @ Ruby関西

Page 2: Ruby初級者向けレッスン 第46回 ─── Test::Unit

お品書き• テスト フレームワーク• テスト駆動開発

– Red–Green–Refactor

• Test::Unit の小技• Test Double

Page 3: Ruby初級者向けレッスン 第46回 ─── Test::Unit

テスト フレームワーク いろいろ• Test::Unit

• RSpec

• Cucumber

• Capybara

• etc.

Page 4: Ruby初級者向けレッスン 第46回 ─── Test::Unit

Test::Unit• プログラムの単体テストを行う• Ruby用の xUnit

– MiniTest::Unit — 標準添付– Test::Unit — 標準添付∗ 内部で MiniTest::Unit を使用

– Test::Unit Gem

Page 5: Ruby初級者向けレッスン 第46回 ─── Test::Unit

RSpec

• プログラムの振舞いを記述する (BDD)

• ドメイン特化言語 (DSL)

• 参考– http://jp.rubyist.net/

magazine/?0021-Rspec

Page 6: Ruby初級者向けレッスン 第46回 ─── Test::Unit

Cucumber (::)

• 自然言語に近い形式で書ける• 受け入れテスト向け• これからは Turnip なの?

http://magazine.rubyist.net/

?0042-FromCucumberToTurnip

Page 7: Ruby初級者向けレッスン 第46回 ─── Test::Unit

Capybara

• Webアプリケーション向け– ブラウザ上のユーザ操作– JavaScript

Page 8: Ruby初級者向けレッスン 第46回 ─── Test::Unit

自動テストのメリット• 機能的な保証

– バグの検出– バグ混入を監視

• 開発支援– 不安を解消– リファクタリング

Page 9: Ruby初級者向けレッスン 第46回 ─── Test::Unit

テスト駆動開発1. まずテストを書き、失敗することを確認する2. テストが成功するように、製品コードを書く3. テストはそのまま、コードをきれいに

Page 10: Ruby初級者向けレッスン 第46回 ─── Test::Unit

Red–Green–Refactor

テスト プロダクト

Red Green

Refactor

-

?�

Page 11: Ruby初級者向けレッスン 第46回 ─── Test::Unit

テスト駆動開発をやってみよう• プロダクトコード — myarray.rb

• テストコード — test myarray.rb

• MyArray が空のときMyArray#empty? は真であるべき

Page 12: Ruby初級者向けレッスン 第46回 ─── Test::Unit

Step 0 プロダクトclass MyArray

def initialize

@array = []

end

end

Page 13: Ruby初級者向けレッスン 第46回 ─── Test::Unit

Step 0 テストrequire ’test/unit’

require ’./myarray’

class TestMyArray < Test::Unit::TestCase

def setup

@array = MyArray.new

end

def test_empty_by_empty_myarray

assert(@array.empty?)

end

end

Page 14: Ruby初級者向けレッスン 第46回 ─── Test::Unit

テストのながれテストごとに、以下を実行する。

1. setup — 準備2. test ~ — テスト本体3. teardown — 後始末

• テストの実行順序は無保証• 各テストは他のテストに依存してはいけない

Page 15: Ruby初級者向けレッスン 第46回 ─── Test::Unit

Step 0 ERROR 実行$ ruby test_myarray.rb

...

E

===================================================

Error: test_empty_by_empty_myarray(TestMyArray)

NoMethodError: undefined method ‘empty?’ for

#<MyArray:0x802ea72c @array=[]>

test_myarray.rb:12:in ‘test_empty_by_empty_myarray’

...

1 tests, 0 assertions, 0 failures, 1 errors,

0 pendings, 0 omissions, 0 notifications

Page 16: Ruby初級者向けレッスン 第46回 ─── Test::Unit

Step 1 プロダクトclass MyArray

def initialize

@array = []

end

def empty? # <= 追加end # <= 追加

end

Page 17: Ruby初級者向けレッスン 第46回 ─── Test::Unit

Step 1 Red 実行$ ruby test_myarray.rb

...

F

===================================================

Failure: <nil> is not true.

test_empty_by_empty_myarray(TestMyArray)

test_myarray.rb:12:in ‘test_empty_by_empty_myarray’

...

1 tests, 1 assertions, 1 failures, 0 errors,

0 pendings, 0 omissions, 0 notifications

Page 18: Ruby初級者向けレッスン 第46回 ─── Test::Unit

Step 2 プロダクトclass MyArray

def initialize

@array = []

end

def empty?

true # <= 追加end

end

Page 19: Ruby初級者向けレッスン 第46回 ─── Test::Unit

Step 2 Fake it! 実行$ ruby test_myarray.rb

Loaded suite test_myarray

Started

.

Finished in 0.000942 seconds.

1 tests, 1 assertions, 0 failures, 0 errors,

0 pendings, 0 omissions, 0 notifications

100% passed

Page 20: Ruby初級者向けレッスン 第46回 ─── Test::Unit

assert式が真なら、テストが通る。

assert("".empty?, "空であること")

assert(obj.nil?, "nil であること")

Page 21: Ruby初級者向けレッスン 第46回 ─── Test::Unit

assert equal

第1引数 == 第2引数 なら、テストが通る。

assert_equal(0, array.size)

assert_equal(’matz’, author)

assert_equal(0.3, 0.2 + 0.1, "Fail!!")

Fail!!

<0.3> expected but was

<0.30000000000000004>

Page 22: Ruby初級者向けレッスン 第46回 ─── Test::Unit

assert in epsilon

第1引数と第2引数の誤差が第3引数の許容範囲内なら、テストが通る。

assert_in_epsilon(0.3, 0.2 + 0.1, 1e-15)

Page 23: Ruby初級者向けレッスン 第46回 ─── Test::Unit

assert raiseブロック内で、第1引数の例外が発生したら、

テストが通る。

ex = assert_raise(IndexError) do

[].fetch(0)

end

Page 24: Ruby初級者向けレッスン 第46回 ─── Test::Unit

Step 3 プロダクトclass MyArray

...

def empty?

true # <= Fake it!

end

def << o # <= 追加@array << o # <= 追加self # <= 追加

end # <= 追加end

Page 25: Ruby初級者向けレッスン 第46回 ─── Test::Unit

Step 3 テスト...

class TestMyArray < Test::Unit::TestCase

...

def test_empty_by_empty_myarray

assert(@array.empty?)

end

def test_empty_by_nonempty_myarray # <= 追加@array << "1st" # <= 追加assert([email protected]?) # <= 追加

end # <= 追加end

Page 26: Ruby初級者向けレッスン 第46回 ─── Test::Unit

Step 3 Triangulate 実行$ ruby test_myarray.rb

...

.F

===================================================

Failure: <false> is not true.

test_empty_by_nonempty_myarray(TestMyArray)

test_myarray.rb:17:in ‘test_empty_by_nonempty_myarray’

...

2 tests, 2 assertions, 1 failures, 0 errors,

0 pendings, 0 omissions, 0 notifications

Page 27: Ruby初級者向けレッスン 第46回 ─── Test::Unit

Step 4 プロダクトclass MyArray

...

def empty?

@array.empty? # <= 修正end

def << o

@array << o

self

end

end

Page 28: Ruby初級者向けレッスン 第46回 ─── Test::Unit

Step 4 Green 実行$ ruby test_myarray.rb

Loaded suite test_myarray

Started

..

Finished in 0.00123 seconds.

2 tests, 2 assertions, 0 failures, 0 errors,

0 pendings, 0 omissions, 0 notifications

100% passed

Page 29: Ruby初級者向けレッスン 第46回 ─── Test::Unit

小技 --help

$ ruby test_myarray.rb --help

Test::Unit automatic runner.

Usage: test_myarray.rb [options] [-- untouched arguments]

-r, --runner=RUNNER Use the given RUNNER.

--collector=COLLECTOR Use the given COLLECTOR.

--ignore-name=NAME Ignores tests matching NAME.

-t, --testcase=TESTCASE Runs tests in TestCases matching TESTCASE.

...

-h, --help Display this help.

Deprecated options:

--console Console runner (use --runner).

Page 30: Ruby初級者向けレッスン 第46回 ─── Test::Unit

小技 --verbose$ ruby test_myarray.rb --verbose

Loaded suite test_myarray

Started

TestMyArray:

test_empty_by_empty_myarray: .: (0.000630)

test_empty_by_nonempty_myarray: .: (0.000499)

Finished in 0.001676 seconds.

2 tests, 2 assertions, 0 failures, 0 errors,

0 pendings, 0 omissions, 0 notifications

100% passed

Page 31: Ruby初級者向けレッスン 第46回 ─── Test::Unit

小技 テストの指定$ ruby test_myarray.rb -v -n /_non/

Loaded suite test_myarray

Started

TestMyArray:

test_empty_by_nonempty_myarray: .: (0.000499)

Finished in 0.000892 seconds.

1 tests, 1 assertions, 0 failures, 0 errors,

0 pendings, 0 omissions, 0 notifications

100% passed

Page 32: Ruby初級者向けレッスン 第46回 ─── Test::Unit

こんなときどうするrequire ’person’

class TestPerson < Test::Unit::TestCase

def test_age

matz = Person.new(

’matz’, Time.local(1965, 4, 14))

assert_equal(48, matz.age)

# 来年はテストに失敗する!?

end

end

Page 33: Ruby初級者向けレッスン 第46回 ─── Test::Unit

Test Double• テスト対象 (プロダクト) が依存している

コンポーネントを置き換える– ダミーオブジェクト– テストスタブ– テストスパイ– モックオブジェクト– フェイクオブジェクト

http://goyoki.hatenablog.com/entry/

20120301/1330608789

Page 34: Ruby初級者向けレッスン 第46回 ─── Test::Unit

例えば影武者を使うrequire ’kagemusha/time’

class TestPerson < Test::Unit::TestCase

def test_age

matz = Person.new(

’matz’, Time.local(1965, 4, 14))

Kagemusha::Time.at(2013, 6, 15) do

assert_equal(48, matz.age)

end

end

end

Page 35: Ruby初級者向けレッスン 第46回 ─── Test::Unit

もしくは Mocha を使うrequire ’mocha/setup’

class TestPerson < Test::Unit::TestCase

def test_age

matz = Person.new(

’matz’, Time.local(1965, 4, 14))

Time.stubs(:now)

.returns(Time.local(2013, 6, 15))

assert_equal(48, matz.age)

end

end

Page 36: Ruby初級者向けレッスン 第46回 ─── Test::Unit

あるいは RR を使うrequire ’rr’

class TestPerson < Test::Unit::TestCase

def test_age

matz = Person.new(

’matz’, Time.local(1965, 4, 14))

mock(Time).now{Time.local(2013, 6, 15)}

assert_equal(48, matz.age)

end

end

Page 37: Ruby初級者向けレッスン 第46回 ─── Test::Unit

まとめ• テスト フレームワーク• テスト駆動開発

– Red–Green–Refactor

• Test::Unit の小技• Test Double

Page 38: Ruby初級者向けレッスン 第46回 ─── Test::Unit

演習問題 0今日のレッスンで分からなかったこと、疑問に思ったことをグループで話し合ってみよう。

Page 39: Ruby初級者向けレッスン 第46回 ─── Test::Unit

演習問題 1• スタッククラスを作ろう!

– テスト駆動開発で「スタック」クラスを作ってみよう。

– push で順に要素を追加できること。– pop で最後から順に要素を取り出せること。– このような動作を LIFO (Last In, First

Out) という。

Page 40: Ruby初級者向けレッスン 第46回 ─── Test::Unit

実装するメソッド• empty?

– スタックが空なら真、空でなければ偽を返す。• size

– スタックの要素数を返す。• push(val)

– 引数の値をスタックの最後に積む• pop

– スタックの最後の値を取り除いて返す。– スタックが空ならStack::EmptyStackErrorを発生させる。

Page 41: Ruby初級者向けレッスン 第46回 ─── Test::Unit

Step 1 empty?

• 新しいスタックの empty? は真であること。• テストが失敗することを確認しよう。Red

• Stack#empty? で真を返そう。Fake it!

Page 42: Ruby初級者向けレッスン 第46回 ─── Test::Unit

Step 2 push pop

• 新しいスタックに 3 を push して、pop すると 3が返ること。

• テストが失敗することを確認しよう。Red

• pop で 3 を返そう。Fake it!

Page 43: Ruby初級者向けレッスン 第46回 ─── Test::Unit

Step 3 size

• 新しいスタックに 3 を push するとsize が 1 になること。

• テストが失敗することを確認しよう。Red

• Stack#size で 1 を返そう。Fake it!

Page 44: Ruby初級者向けレッスン 第46回 ─── Test::Unit

Step 4 size

• 新しいスタックに 3 を push した後、5 を push すると size が 2 になること。

• Fake it! が通らなくなることを確認しよう。Triangulate

• テストが通るように修正しよう。

Page 45: Ruby初級者向けレッスン 第46回 ─── Test::Unit

Step 5 empty?

• 新しいスタックに 3 を push するとempty? は偽であること。

• Fake it! が通らなくなることを確認しよう。Triangulate

• テストが通るように修正しよう。

Page 46: Ruby初級者向けレッスン 第46回 ─── Test::Unit

Step 6 pop

• 新しいスタックから pop するとStack::EmptyStackErrorが発生すること。

Page 47: Ruby初級者向けレッスン 第46回 ─── Test::Unit

Step 7 pop

• 新しいスタックに 3 を push した後、5 をpush し、pop すると size が 1 であること。

Page 48: Ruby初級者向けレッスン 第46回 ─── Test::Unit

Step 8 pop

• 新しいスタックに 3 を push した後、5 を push し、pop すると 5 が返ること。

Page 49: Ruby初級者向けレッスン 第46回 ─── Test::Unit

自己紹介• 名前 (ニックネーム)

• 普段の仕事・研究内容・代表作• Ruby歴・コンピュータ歴• 勉強会に来た目的• などなど

Page 50: Ruby初級者向けレッスン 第46回 ─── Test::Unit

参考• るりまhttp://doc.ruby-lang.org/ja/

• サンプルコードhttps://github.com/higaki/

learn ruby kansai 58

• XP祭り関西にてユニットテストの保守に関する発表http://goyoki.hatenablog.com/entry/

20110202/1296663870