2012年7月11日水曜日

web2py でのテスト ① doctestの使い方

今回は web2py での テスト について簡単に触れます。

テストの必要性

テストはプログラムの品質向上のためにも、TDD(テスト駆動開発)の観点からも必要とされている。このため web2py ではテストツールとして、 doctest , unittest などが使われているようだ。また一部では unittest を拡張し使いやすくした、nose も利用しているようである。

プログラム品質向上やTDDに本格的に取り組むためには、unittest や nose といった高機能のテストツールをガンガン使いこなして開発を進めていく必要があるが、今回は比較的単純なテストツールである doctest を利用した web2py のテストについて触れていく。

doctest を使用する理由は、単純なため取っ付きやすいのと、構文テストのためである。

構文テストはコンパイル言語の場合、コンパイル時に構文チェックが動作し、コンパイルエラーで問題がある箇所を指摘してくれる。
しかしスクリプト言語では通常は、事前に構文のチェックは行われない。Pythonの構文チェックだけなら pylint というツールで実行可能だが、web2pyのクラスや関数と組み合わせたチェックや、作成した関数のパラメータチェックなどは行わない。

参考:web2py アプリケーションを Editra で開発する ① Editraプラグインの機能

このためプログラム内部で利用する関数のパラメータを変更した場合などに、動作確認の最低限のチェックのため、テストを実施する必要がある。

web2py ではフレームワークに、doctest を実行する仕組みが組み込まれている。このため簡単に利用が可能だ。しかし実際にweb2py と組み合わせてテストを実施する場合、いろいろなエラーで悩んだりする。これらも併せて、ちょっとしたテクニックを紹介していく。

web2py での doctest の利用 (管理画面)

web2pyフレームワーク上での doctest 利用手順を紹介する。

1. コードを追加
まず簡単な doctest のコードを書いてみる。
1
2
>>> index().has_key('message')
True
は index 関数が、message というキーを持つ辞書型データを返すことをテストする。

生成したアプリケーションの default.py コントローラファイルの index 関数に、次のようにコードを貼り付ける(5・6行目)。

1
2
3
4
5
6
7
8
9
def index():
    """
    example action using the internationalization operator T and flash
    rendered by views/default/index.html or views/generic.html
    >>> index().has_key('message')
    True
    """
    response.flash = "Welcome to web2py!"
    return dict(message=T('Hello World'))
doctestのコードは、関数のドキュメント文字列中に記述するのが一般的だ。

2. 管理画面での実行
記述した doctest を実行するには、web2py 管理画面のコントローラ項目の下にある「test(テスト)」ボタンを押すか、コントローラファイル名の横にある黄色い「test」アイコンを押す。testボタンはコントローラの全ての doctest を実行する。コントローラファイルの横の test アイコンは、特定のコントローラファイルの dctest のみを実行する。

3. 管理画面での結果参照
実行結果は、次のように正常完了した場合は関数名の先頭についているアイコンが青く表示される。エラーがあった場合は、エラー内容が表示される。また灰色のアイコンが表示されいる関数は、doctest の記述が無かった関数を示している。

■ 正常完了した時

■ エラーがあった時

web2py での doctest の利用 (コマンドライン)

コマンドライン(pythonシェル)で web2py 環境の doctest を実行することもできる。この場合のアドバンテージとして、デバッガーが利用可能だ。

ドキュメント文字列に doctest コードが記述された関数を含むコントロールファイルを、次のようにコマンドラインから実行する。

C:\Users\xxx\web2py>python web2py.py -T myapp/default
web2py Web Framework
Created by Massimo Di Pierro, Copyright 2007-2011
Version 1.99.7 (2012-03-04 22:12:08) stable
Database drivers available: SQLite3, pymysql, pg8000, IMAP

C:\Users\xxx\web2py>

コマンドラインから dcotest を実行するには web2py.py を、オプション T を付けて実行する。

オプション T の後には、テスト対象のアプリケーション名を指定する。

C:\Users\xxx\web2py>python web2py.py -T myapp

特定のコントロールファイルの doctest のみを実行するには、アプリケーション名の後にコントローラ名も指定する。

C:\Users\xxx\web2py>python web2py.py -T myapp/default

特定の関数のみを実行するには、コントローラ名の後に関数名を指定する。

C:\Users\xxx\web2py>python web2py.py -T myapp/default/index

この他、実行結果の詳細を表示させるには、オプション v を付けて実行する。

C:\Users\xxx\web2py>python web2py.py -vT myapp/default
web2py Web Framework
Created by Massimo Di Pierro, Copyright 2007-2011
Version 1.99.7 (2012-03-04 22:12:08) stable
Database drivers available: SQLite3, pymysql, pg8000, IMAP
Finding tests in default.py: download
Finding tests in default.py: index
Trying:
    index().has_key('message')
Expecting:
    True
ok
Finding tests in default.py: call
Finding tests in default.py: user
Finding tests in default.py: data
doctest でのデバッグ

doctest でのデバッグは、デバッグを開始したい箇所に次のように、set_trace() を挿入することで実施可能だ。

import pdb; pdb.set_trace()

デバッグ実行は、コマンドラインから該当するコードを含むテストを実行する必要がある。

注意点として、デバッグの動作範囲(スコープ)はコマンド単位になっている。このため、やみくもに set_trace() を挿入しても上手くデバッグはできない。例として、上記の doctest コードでデバッグを行なってみる。

まず、上記の doctestコードを次のように変更する。

1
2
3
4
5
>>> def f(text):
...     import pdb; pdb.set_trace()
...     return index().has_key(text)
>>> f('message') 
True

そして、doctestをコマンドラインから実行してみる。

C:\Users\xxx\web2py>python web2py.py -T myapp/default
web2py Web Framework
Created by Massimo Di Pierro, Copyright 2007-2011
Version 1.99.7 (2012-03-04 22:12:08) stable
Database drivers available: SQLite3, pymysql, pg8000, IMAP
> <doctest default.py: index[0]>(3)f()
-> return index().has_key(text)
(Pdb) l
  1     def f(text):
  2         import pdb; pdb.set_trace()
  3  ->     return index().has_key(text)
[EOF]
(Pdb) p text
'message'
(Pdb) n
--Return--
> <doctest default.py: index[0]>(3)f()->True
-> return index().has_key(text)
(Pdb) c

C:\Users\xxx\web2py>

デバッグ対象を関数として定義することにより、pdb コマンドを使ってデバッグを実行できることがわかる。これはデバッグの動作範囲がコマンド単位なので、デバッグしたいコードを関数定義でブロック化することにより、関数内でデバッグを可能にしている。

また、pdb コマンドについては、こちらでも簡単に紹介している。

参考: web2py でデバッグツールを利用する ①


随分長くなりましたので、今回の記事はここで終わります。次回にどのように doctest コードを書いたらよいのか解説していきたいと思います。