Upload
yoshiki-shibukawa
View
1.566
Download
2
Embed Size (px)
Citation preview
PythonDeveloperCamp 2008
ユニットテスト -2 日目 -
渋川よしき
※1日目からの流用も多いです。最初に言い訳しておきます。
PythonDeveloperCamp 2008ユニットテスト2日目 Page 2
まずは
目的- 昨日の宿題 (doctest, ユニットテストのメリット )
- ビヘイビア駆動開発とは何者かを知る
- PySpec を使ってみる
- モックオブジェクトなどの周辺技術も知る
やって欲しいこと※
- 体験→発見
- いつもと違ったことをする
- 質問する
- 失敗はない、学びがある
- 楽しむ ※六本木ヒルズでやっている NLPセミナーの手法を参考にしてます
PythonDeveloperCamp 2008ユニットテスト2日目 Page 3
チェックイン※
ペアを作ってください- 二人一組で開発してもらいます。昨日とは違う人と組んでください。
- やろうとすること、考えていることを口に出すと学習効果アップ※
自己紹介 (2 人で 2 分 )
- 自分の名前 or ハンドルネーム
- Python との関わり
- この1時間で何を学びたいか?という決意表明をしてください
※僕が勝手に師匠と呼んでいる方の スライドを参考にしました※「言語技術」が日本のサッカーを変える
PythonDeveloperCamp 2008ユニットテスト2日目 Page 4
( 参考 ) 昨日作った、一人分のルールエンジンクラス
class Cricket(object):
def __init__(self):
self.areas = {18:0, 20:0}
def throw(self, area, count):
if area not in self.areas:
return
self.areas[area] += count
def get_count(self, area):
return self.areas[area]
def is_closed(self, area):
return self.get_count(area) >= 3
def is_finished(self):
return self.is_closed(18) and self.is_closed(20)
PythonDeveloperCamp 2008
doctest を使ってみよう
ユニットテスト1日目 Page 5
PythonDeveloperCamp 2008
doctest
doctest とは?- docstring の中のテストコードを実行する仕
組み
- コメントの中に自然に書ける
- インタラクティブモードをコピペすればテスト完成
- 文芸的なテスト ( 造語 )
- Python が発祥?
import doctest
def pow(x, y):
"""calc power.
>>> pow(1, 2)
1
>>> pow(10, 2)
100
"""
result = 1
for i in xrange(y):
result *= x
return result
if __name__ == '__main__':
doctest.testmod()
PythonDeveloperCamp 2008
doctest を書いてみよう
例題:先ほど作ったクリケットのクラスのテスト- ダーツを投げてみて、クリアするまでの過程をインタラクティブモードで実行
します
- それをコピペして、 docstring に書き込みます。
- もしも unittest のスタートアップを書いていた場合には doctest のスタートアップに切り替えます。
実行の仕方- スタートアップコードを書いた場合にはそのままコンソールから実行できます
- エラーがないと何も画面には出ません
- -v をつけると、実況中継が出ます
PythonDeveloperCamp 2008
doctest に関する渋川の所感
メリット- 気軽に書ける
- コメントの中にかけるので、自然言語で補強できる
- ユーザの目に触れやすい
- ライブラリ系ならば動作可能なドキュメント / サンプルコードとして使える
デメリット- 複雑なテストケースが書きにくい
- 網羅的なテストケースが書きにくい ( 1オブジェクトにつき数パターンが限度? )
- return で結果がきれい出るものじゃないと書きにくい ( 結果が DOM ツリーになるとか )
PythonDeveloperCamp 2008
ユニットテストの利点
PythonDeveloperCamp 2008ユニットテスト2日目 Page 10
ユニットテストの利点渋川の考えていること
脳の右側を使う- 子供にパズルをさせる
- 何か欠けている
- 完成形が見える
- 何をすればいいのかが分かる
余計な脳を使わない- 脳の処理能力は意識しないところでも使われる
- 無駄なことを視界からはずすことで効率を上げる→ GTD
品質アップ- ポカよけ
- リファクタリングを円滑にすすめるためのセーフティネット
- テストのないリファクタリングはスリリングで危ない楽しさがある
PythonDeveloperCamp 2008
ディレクティブなテストの歴史
※説明は飛ばすので、暇なときにでもお読みください
PythonDeveloperCamp 2008ユニットテスト2日目 Page 12
xUnit の歴史
SUnit と JUnit
- SUnit は eXtreme Programming で有名な Kent Beck が作成
- フレームワークが持っている特徴はすでに完成していた
- TestCase を継承、 test で始まるメソッド名、 assert 文
- JUnit は Kent Beck と Eric Gamma( デザパタの人 ) が作成
xUnitへ- XP 本 (1999) の広がりとともに、いろんな言語に移植された
- pyunit も 1999 に開発され、 Python2.1(Apr/2001) にバンドル
PythonDeveloperCamp 2008ユニットテスト2日目 Page 13
TDD から BDD
BDD が登場- 2003年に jBehave というフレームワークが開発された
- 2004年に平鍋さんが日本に紹介
- 懸田さんの翻訳記事はかなり重要
- Test/assert を、 should/verify に改名
- テストじゃなくて仕様なんだ!というのを強調
- サピアウォーフの仮説という言葉が一瞬流行った
rSpec
- 2005年に rSpec の開発が開始
- 活発に開発されていて、今 BDD 界をリードしているのは rSpec( っぽい )
PythonDeveloperCamp 2008ユニットテスト2日目 Page 14
PySpec
PyUnitX
- unittest.py が古くさく感じたので、デコレータでテスト宣言をするフレームワークとして 2006年に開発開始
- 2006年のオブジェクト倶楽部夏イベントのライトニングトークスでデモ
- BDD っぽいテストの書き方も追加機能で実装
PySpecへ- BDD機能のみに絞り、 TDD っぽい名前は削除
- 一度完全にスクラップにしてリビルドした
- Python Workshop The Edge 2007のトークスで発表
- 2008年1月にCodePlexで公開→引っ越し予定
PythonDeveloperCamp 2008
PySpec でユニットテストを書いてみよう
PythonDeveloperCamp 2008Here comes your footer Page 16
例題:ダーツのクリケット
15~ 20 、ブル ( 中心 ) だけを使う 交互に投げる 1つの数字に3本入ると占領 ( クロー
ズ )
- ダブルは2本分、トリプルは3本分
- 全員が一つの数字をクローズするとキル
- クローズしてキルされるまではスコアが入る
誰かが全部をクローズすると終了- スコアが高い人の勝ち
- 最大 20 ラウンド
引用元: Wikipedia
PythonDeveloperCamp 2008Here comes your footer Page 17
例題:ダーツのクリケット (今回のワークの範囲 )
15~ 20 、ブル ( 中心 ) だけを使う→ 18, 20
交互に投げる→ 2 人プレイ 1つの数字に3本入ると占領 ( クローズ )
- ダブルは2本分、トリプルは3本分
- 全員が一つの数字をクローズするとキル
- クローズしてキルされるまではスコアが入る
誰かが全部をクローズすると終了- スコアが高い人の勝ち
- 最大 20 ラウンド
引用元: Wikipedia
PythonDeveloperCamp 2008ユニットテスト2日目 Page 18
まずは準備
ファイルの構成はどちらでもいいです。- 実際のコードとテストコードでファイル分割
- テストコードも実際のコードも同じファイルにする
最初のテストをパスするところまではデモします。真似してタイプしてみてください
二つめ以降は画面に出しませんので、挑戦してみてください
# -*- coding: utf-8 -*-
from pyspec import *
# スタートアップコード
if __name__ == “__main__”:
run_test()
PythonDeveloperCamp 2008ユニットテスト2日目 Page 19
テスト駆動開発の基本ルール→基本は TDD と一緒
テストファースト- Python が「コードを書いてくれ~」と言ってからコードを書く
- 具体的には AttributeError, AssertionError など
仮実装- 返値が 3 なら、 return 3 と書いてしまう
三角測量- 一つのメソッドにつき2つのテストケースを使う
明白な実装- 分かり切っている実装は仮実装を行わないでいきなり実装
仕様が分かるメソッド名前- 名前だけで仕様書の記述ができるようにする。コンテキストと仕様は分ける
PythonDeveloperCamp 2008ユニットテスト2日目 Page 20
最初のメソッドを実装するまで仕様を記述する
テストクラスは普通の Python のクラス 準備は @context, 仕様は @spec というデコレータをつける About().should_equal() などを使ってテストを書いていきます。 準備と仕様はなるべく分けます。仕様書として、事前条件、事後条件を分けて
書く
class Behavior_CricketGame(object):
@context
def a_new_game(self):
self.game = CricketGame()
@spec
def which_current_player_is_no1(self):
About(self.game.current()).should_equal(0)
PythonDeveloperCamp 2008
最初のメソッドを実装するまでFailure を見る→仮実装で OK を見る (TDD と一緒 )
次に、 NameError( クラスが無い ) と言われるので、クラスを実装する。
NameError→AttributeError( メソッドがない ) とひとつずつ解決していくと、 Error が Failure( テスト失敗 ) に変化する
Failure を見たら、仮実装 ( テストを通るだけの最低限 ) をしてテストを OK にする
ユニットテスト2日目 Page 21
class CricketGame(object):
def current(self):
pass
PythonDeveloperCamp 2008ユニットテスト2日目 Page 22
最初のメソッドを実装するまで三角測量でメソッドを完成させる
もう一つテストを書いて、実装を完成させます (三角測量 )
- 第一ステップ:最初のプレイヤーは1 ( 最初の仮実装 )
- 第二ステップ:一回投げるとプレイヤー2 (今回のステップ )
@context(group=2)
def a_game_that_was_thrown_once(self):
self.game = CricketGame()
self.game.throw(18, 1)
@spec(group=2)
def which_current_is_no2(self):
About(self.game.current()).should_equal(1)
PythonDeveloperCamp 2008Here comes your footer Page 23
例題:ダーツのクリケット (今回のワークの範囲 )
15~ 20 、ブル ( 中心 ) だけを使う→ 18, 20
交互に投げる→ 2 人プレイ 1つの数字に3本入ると占領 ( クローズ )
- ダブルは2本分、トリプルは3本分
- 全員が一つの数字をクローズするとキル
- クローズしてキルされるまではスコアが入る
誰かが全部をクローズすると終了- スコアが高い人の勝ち
- 最大 20 ラウンド
引用元: Wikipedia
PythonDeveloperCamp 2008
PySpec 周辺機能のデモ
MockObject, レガシーコードテスト
ユニットテスト2日目 Page 24
PythonDeveloperCamp 2008
MockObject
MockObject(模造オブジェクト ) とは?- まだ無いオブジェクトの振りをして、もうすでにあるかのように動作する
- 外部からの操作 ( メソッド呼び出し ) が適正か調べる
- 調べる機能がないものはスタブと呼ばれる
- 親から呼ばれる子供オブジェクトに使うことが多い
なぜ作ったのか?- Ruby の dRuby の実装を見て、そのアイディアを持ってきた
- 好きなメソッドは method_missing
- PySpec 自身のテストを書くのに必要だった
ユニットテスト2日目 Page 25
PythonDeveloperCamp 2008ユニットテスト2日目 Page 26
MockObject のサンプル
Record&Verify
- 第一ステップ:最初のプレイヤーは1 ( 最初の仮実装 )
- 第二ステップ:一回投げるとプレイヤー2 (今回のステップ )
from pyspec.mockobject import *
recorder = MockObjectRecorder()
recorder.throw(18, 1)
recorder.get_count(18) == 1
mock = recorder._get_mock_object_()
mock.throw(18, 2) <= エラー
mock._verify_() <= 呼び残しがあればエラー
PythonDeveloperCamp 2008
レガシーコードテスト機能
レガシーコードテストの仕組み- 既存の振る舞いを正として、コードが壊れてないか調べる
- プログラマの意図を表現したユニットテストに比べたら補助的機能
- 徐々に正規のユニットテストに置き換えていくことを想定
- 一度目の実行結果を記録する
レガシーコードテストのための機能- About().should_not_be_changed()
- 値が変更していないことを調査する
- pyspec.legacycode.test_proxy()
- オブジェクトに対するプロキシとして動作し呼ばれたメソッドを記録・検証する
ユニットテスト2日目 Page 27
PythonDeveloperCamp 2008ユニットテスト2日目 Page 28
レガシーコードテスト機能のサンプル
should_not_be_changed()
test_proxy()
cricket = test_proxy(Cricket())
# メソッド呼び出し、返り値を監視して、異なっていればエラー
cricket.throw(18, 2)
cricket.get_score(18)
cricket.finished()
a = 10
About(a).should_not_be_changed() #絶対成功する例
b = random.randint(0, 100)
About(b).should_not_be_changed() #絶対失敗する例
PythonDeveloperCamp 2008
クロージング
2日間お疲れ様でした
ユニットテスト2日目 Page 29
PythonDeveloperCamp 2008
よりよいテストを書くために
見やすいテスト- 準備と結果 ( テスト ) を明確に分離する
- 文字列と変数重要 ( 人も PC も読んで分かる )
- 必要に応じて裏口 ( テスト用のインタフェース ) を作る
- PySpec の中にもたくさん裏口があります
- テストもリファクタリングして読みやすいように
これからの挑戦- UI のテストをもっとシンプルに
- テスト結果を、非開発者が読んで意味のある情報として出力する
- カバレッジの表示など、もっとテストしたくなる機能の追加
- 間口を広げる (doctest統合など )
ユニットテスト2日目 Page 30
PythonDeveloperCamp 2008
宿題
周りの人とユニットテストについて議論してみましょう。
ダーツのクリケット- 残りのルールもすべて実装してみてください
ボウリング- ユニットテストのサンプルとして人気の、ボウリングのスコア計算を作ってみて
ください
100 本ユニットテスト※
- ユニットテストを 100 本書いてみて下さい※MindMapのセミナーの宿題を 参考にしました
ユニットテスト2日目 Page 31
PythonDeveloperCamp 2008
参考資料これからユニットテストを深めていくために
Python のライブラリリファレンス unittest, doctest
デブサミ2008 和田卓人氏「デベロッパーテスティング・ライブ」
JPUG北海道支部/Ruby札幌合同 角谷信太郎氏「スはスペックのス」
Google→TDD, ユニットテスト etc
http://www.codeplex.com/pyspec/ - 引っ越し予定 まさーる氏 Kent Beck Testing Framework入門 ( 渋川の原点 ) – 1999年
ユニットテスト2日目 Page 32