32
PythonDeveloperCamp 2008 ユユユユユユユ -2 ユユ - ユユユユユ 日日日日日日日日日日日日日 日日日日日日日日日日日日日 1。。

ユニットテスト_2日目

Embed Size (px)

Citation preview

Page 1: ユニットテスト_2日目

PythonDeveloperCamp 2008

ユニットテスト -2 日目 -

渋川よしき

※1日目からの流用も多いです。最初に言い訳しておきます。

Page 2: ユニットテスト_2日目

PythonDeveloperCamp 2008ユニットテスト2日目 Page 2

まずは

目的- 昨日の宿題 (doctest, ユニットテストのメリット )

- ビヘイビア駆動開発とは何者かを知る

- PySpec を使ってみる

- モックオブジェクトなどの周辺技術も知る

やって欲しいこと※

- 体験→発見

- いつもと違ったことをする

- 質問する

- 失敗はない、学びがある

- 楽しむ ※六本木ヒルズでやっている  NLPセミナーの手法を参考にしてます

Page 3: ユニットテスト_2日目

PythonDeveloperCamp 2008ユニットテスト2日目 Page 3

チェックイン※

ペアを作ってください- 二人一組で開発してもらいます。昨日とは違う人と組んでください。

- やろうとすること、考えていることを口に出すと学習効果アップ※

自己紹介 (2 人で 2 分 )

- 自分の名前 or ハンドルネーム

- Python との関わり

- この1時間で何を学びたいか?という決意表明をしてください

※僕が勝手に師匠と呼んでいる方の スライドを参考にしました※「言語技術」が日本のサッカーを変える

Page 4: ユニットテスト_2日目

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)

Page 5: ユニットテスト_2日目

PythonDeveloperCamp 2008

doctest を使ってみよう

ユニットテスト1日目 Page 5

Page 6: ユニットテスト_2日目

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()

Page 7: ユニットテスト_2日目

PythonDeveloperCamp 2008

doctest を書いてみよう

例題:先ほど作ったクリケットのクラスのテスト- ダーツを投げてみて、クリアするまでの過程をインタラクティブモードで実行

します

- それをコピペして、 docstring に書き込みます。

- もしも unittest のスタートアップを書いていた場合には doctest のスタートアップに切り替えます。

実行の仕方- スタートアップコードを書いた場合にはそのままコンソールから実行できます

- エラーがないと何も画面には出ません

- -v をつけると、実況中継が出ます

Page 8: ユニットテスト_2日目

PythonDeveloperCamp 2008

doctest に関する渋川の所感

メリット- 気軽に書ける

- コメントの中にかけるので、自然言語で補強できる

- ユーザの目に触れやすい

- ライブラリ系ならば動作可能なドキュメント / サンプルコードとして使える

デメリット- 複雑なテストケースが書きにくい

- 網羅的なテストケースが書きにくい ( 1オブジェクトにつき数パターンが限度? )

- return で結果がきれい出るものじゃないと書きにくい ( 結果が DOM ツリーになるとか )

Page 9: ユニットテスト_2日目

PythonDeveloperCamp 2008

ユニットテストの利点

Page 10: ユニットテスト_2日目

PythonDeveloperCamp 2008ユニットテスト2日目 Page 10

ユニットテストの利点渋川の考えていること

脳の右側を使う- 子供にパズルをさせる

- 何か欠けている

- 完成形が見える

- 何をすればいいのかが分かる

余計な脳を使わない- 脳の処理能力は意識しないところでも使われる

- 無駄なことを視界からはずすことで効率を上げる→ GTD

品質アップ- ポカよけ

- リファクタリングを円滑にすすめるためのセーフティネット

- テストのないリファクタリングはスリリングで危ない楽しさがある

Page 11: ユニットテスト_2日目

PythonDeveloperCamp 2008

ディレクティブなテストの歴史

※説明は飛ばすので、暇なときにでもお読みください

Page 12: ユニットテスト_2日目

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) にバンドル

Page 13: ユニットテスト_2日目

PythonDeveloperCamp 2008ユニットテスト2日目 Page 13

TDD から BDD

BDD が登場- 2003年に jBehave というフレームワークが開発された

- 2004年に平鍋さんが日本に紹介

- 懸田さんの翻訳記事はかなり重要

- Test/assert を、 should/verify に改名

- テストじゃなくて仕様なんだ!というのを強調

- サピアウォーフの仮説という言葉が一瞬流行った

rSpec

- 2005年に rSpec の開発が開始

- 活発に開発されていて、今 BDD 界をリードしているのは rSpec( っぽい )

Page 14: ユニットテスト_2日目

PythonDeveloperCamp 2008ユニットテスト2日目 Page 14

PySpec

PyUnitX

- unittest.py が古くさく感じたので、デコレータでテスト宣言をするフレームワークとして 2006年に開発開始

- 2006年のオブジェクト倶楽部夏イベントのライトニングトークスでデモ

- BDD っぽいテストの書き方も追加機能で実装

PySpecへ- BDD機能のみに絞り、 TDD っぽい名前は削除

- 一度完全にスクラップにしてリビルドした

- Python Workshop The Edge 2007のトークスで発表

- 2008年1月にCodePlexで公開→引っ越し予定

Page 15: ユニットテスト_2日目

PythonDeveloperCamp 2008

PySpec でユニットテストを書いてみよう

Page 16: ユニットテスト_2日目

PythonDeveloperCamp 2008Here comes your footer Page 16

例題:ダーツのクリケット

15~ 20 、ブル ( 中心 ) だけを使う 交互に投げる 1つの数字に3本入ると占領 ( クロー

ズ )

- ダブルは2本分、トリプルは3本分

- 全員が一つの数字をクローズするとキル

- クローズしてキルされるまではスコアが入る

誰かが全部をクローズすると終了- スコアが高い人の勝ち

- 最大 20 ラウンド

引用元: Wikipedia

Page 17: ユニットテスト_2日目

PythonDeveloperCamp 2008Here comes your footer Page 17

例題:ダーツのクリケット (今回のワークの範囲 )

15~ 20 、ブル ( 中心 ) だけを使う→ 18, 20

交互に投げる→ 2 人プレイ 1つの数字に3本入ると占領 ( クローズ )

- ダブルは2本分、トリプルは3本分

- 全員が一つの数字をクローズするとキル

- クローズしてキルされるまではスコアが入る

誰かが全部をクローズすると終了- スコアが高い人の勝ち

- 最大 20 ラウンド

引用元: Wikipedia

Page 18: ユニットテスト_2日目

PythonDeveloperCamp 2008ユニットテスト2日目 Page 18

まずは準備

ファイルの構成はどちらでもいいです。- 実際のコードとテストコードでファイル分割

- テストコードも実際のコードも同じファイルにする

最初のテストをパスするところまではデモします。真似してタイプしてみてください

二つめ以降は画面に出しませんので、挑戦してみてください

# -*- coding: utf-8 -*-

from pyspec import *

# スタートアップコード

if __name__ == “__main__”:

run_test()

Page 19: ユニットテスト_2日目

PythonDeveloperCamp 2008ユニットテスト2日目 Page 19

テスト駆動開発の基本ルール→基本は TDD と一緒

テストファースト- Python が「コードを書いてくれ~」と言ってからコードを書く

- 具体的には AttributeError, AssertionError など

仮実装- 返値が 3 なら、 return 3 と書いてしまう

三角測量- 一つのメソッドにつき2つのテストケースを使う

明白な実装- 分かり切っている実装は仮実装を行わないでいきなり実装

仕様が分かるメソッド名前- 名前だけで仕様書の記述ができるようにする。コンテキストと仕様は分ける

Page 20: ユニットテスト_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)

Page 21: ユニットテスト_2日目

PythonDeveloperCamp 2008

最初のメソッドを実装するまでFailure を見る→仮実装で OK を見る (TDD と一緒 )

次に、 NameError( クラスが無い ) と言われるので、クラスを実装する。

NameError→AttributeError( メソッドがない ) とひとつずつ解決していくと、 Error が Failure( テスト失敗 ) に変化する

Failure を見たら、仮実装 ( テストを通るだけの最低限 ) をしてテストを OK にする

ユニットテスト2日目 Page 21

class CricketGame(object):

def current(self):

pass

Page 22: ユニットテスト_2日目

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)

Page 23: ユニットテスト_2日目

PythonDeveloperCamp 2008Here comes your footer Page 23

例題:ダーツのクリケット (今回のワークの範囲 )

15~ 20 、ブル ( 中心 ) だけを使う→ 18, 20

交互に投げる→ 2 人プレイ 1つの数字に3本入ると占領 ( クローズ )

- ダブルは2本分、トリプルは3本分

- 全員が一つの数字をクローズするとキル

- クローズしてキルされるまではスコアが入る

誰かが全部をクローズすると終了- スコアが高い人の勝ち

- 最大 20 ラウンド

引用元: Wikipedia

Page 24: ユニットテスト_2日目

PythonDeveloperCamp 2008

PySpec 周辺機能のデモ

MockObject, レガシーコードテスト

ユニットテスト2日目 Page 24

Page 25: ユニットテスト_2日目

PythonDeveloperCamp 2008

MockObject

MockObject(模造オブジェクト ) とは?- まだ無いオブジェクトの振りをして、もうすでにあるかのように動作する

- 外部からの操作 ( メソッド呼び出し ) が適正か調べる

- 調べる機能がないものはスタブと呼ばれる

- 親から呼ばれる子供オブジェクトに使うことが多い

なぜ作ったのか?- Ruby の dRuby の実装を見て、そのアイディアを持ってきた

- 好きなメソッドは method_missing

- PySpec 自身のテストを書くのに必要だった

ユニットテスト2日目 Page 25

Page 26: ユニットテスト_2日目

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_() <= 呼び残しがあればエラー

Page 27: ユニットテスト_2日目

PythonDeveloperCamp 2008

レガシーコードテスト機能

レガシーコードテストの仕組み- 既存の振る舞いを正として、コードが壊れてないか調べる

- プログラマの意図を表現したユニットテストに比べたら補助的機能

- 徐々に正規のユニットテストに置き換えていくことを想定

- 一度目の実行結果を記録する

レガシーコードテストのための機能- About().should_not_be_changed()

- 値が変更していないことを調査する

- pyspec.legacycode.test_proxy()

- オブジェクトに対するプロキシとして動作し呼ばれたメソッドを記録・検証する

ユニットテスト2日目 Page 27

Page 28: ユニットテスト_2日目

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() #絶対失敗する例

Page 29: ユニットテスト_2日目

PythonDeveloperCamp 2008

クロージング

2日間お疲れ様でした

ユニットテスト2日目 Page 29

Page 30: ユニットテスト_2日目

PythonDeveloperCamp 2008

よりよいテストを書くために

見やすいテスト- 準備と結果 ( テスト ) を明確に分離する

- 文字列と変数重要 ( 人も PC も読んで分かる )

- 必要に応じて裏口 ( テスト用のインタフェース ) を作る

- PySpec の中にもたくさん裏口があります

- テストもリファクタリングして読みやすいように

これからの挑戦- UI のテストをもっとシンプルに

- テスト結果を、非開発者が読んで意味のある情報として出力する

- カバレッジの表示など、もっとテストしたくなる機能の追加

- 間口を広げる (doctest統合など )

ユニットテスト2日目 Page 30

Page 31: ユニットテスト_2日目

PythonDeveloperCamp 2008

宿題

周りの人とユニットテストについて議論してみましょう。

ダーツのクリケット- 残りのルールもすべて実装してみてください

ボウリング- ユニットテストのサンプルとして人気の、ボウリングのスコア計算を作ってみて

ください

100 本ユニットテスト※

- ユニットテストを 100 本書いてみて下さい※MindMapのセミナーの宿題を  参考にしました

ユニットテスト2日目 Page 31

Page 32: ユニットテスト_2日目

PythonDeveloperCamp 2008

参考資料これからユニットテストを深めていくために

Python のライブラリリファレンス unittest, doctest

デブサミ2008 和田卓人氏「デベロッパーテスティング・ライブ」

JPUG北海道支部/Ruby札幌合同 角谷信太郎氏「スはスペックのス」

Google→TDD, ユニットテスト etc

http://www.codeplex.com/pyspec/ - 引っ越し予定 まさーる氏 Kent Beck Testing Framework入門 ( 渋川の原点 ) – 1999年

ユニットテスト2日目 Page 32