2013年4月19日金曜日

web2py でのテスト ③ モジュールでのdoctestの実行方法

かなり久しぶりの投稿です。以前、次のような doctest に関する記事を投稿しました。

今回は モジュール での doctest の実行方法について書いてみます。

モジュールについて

最初に web2py のモジュールについて、簡単に説明します。モジュールは、共通関数などを抜き出し、独立性の高いコードとして設定するものです。 web2py管理画面の「モジュール」というセクションで、モジュールファイルの作成やアップロードが可能になっています。

モジュールを利用する場合の注意点を少し書いてみます。

モデル変数の扱い
モジュールはコントローラやモデルでのコード記述とは違い、モデルで定義した変数にはアクセスできません。このためモジュールで記述する関数やクラスで、 モデル定義の変数(db や auth など)を使用したい場合は、パラメータでこれらの変数を渡してやる current変数に渡してやる 必要があります(注:記述が間違っていました web2py book モジュールからのAPIアクセスを参照)。
Pythonモジュールの利用
easy_install や pip などで導入する Python モジュールを、web2py 内に設定し利用することが可能です。この場合は管理画面のモジュールを利用するのでは 無く、web2py ディレクトリの site-packages に設定します。具体的には次のページで設定方法を紹介していますので、 参考にしてください。
Google AppEngine での Python-OpenID モジュールの設定

これは本番の Python 環境で、モジュールを追加できない場合などに利用可能です。

コード開発時に便利な設定
モジュールは一度インポートされると、モジュールのコード変更しても再度読み込まれることはありません。 しかしこのような動作は、コード開発時には不都合です。 もし次のコードをモデル定義に記述しておけば、モジュールのインポート命令がある度に、コードの変更チェックを行います。 変更がある場合は、モジュールのリロードを行います。
from gluon.custom_import import track_changes; track_changes(True)

本番環境ではモジュールのリロードは不要ですので、コードをコメントにします。

モジュールテストの例

さて本題のモジュールのテストについて、入っていきます。まず date_calculator.py というモジュールファイルを生成し、次のコードを記述します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#!/usr/bin/env python
# coding: utf8
from gluon import *

def last_friday(date=None):
    '''
    >>> last_friday()    #doctest: +ELLIPSIS
    datetime.date(...
    >>> last_friday('20130101')
    datetime.date(2012, 12, 28)
    >>> last_friday(20130101)
    Traceback (most recent call last):
    ...
    TypeError: must be string, not int
    >>> last_friday('2013/01/01')    #doctest: +ELLIPSIS
    Traceback (most recent call last):
    ...
    ValueError: ...
    '''
    return retrieve_date(init_date=date, weekday=4, weeks=-1)

def next_monday(date=None):
    '''
    >>> next_monday()    #doctest: +ELLIPSIS
    datetime.date(...
    >>> next_monday('20130101')
    datetime.date(2013, 1, 7)
    >>> next_monday(20130101)
    Traceback (most recent call last):
    ...
    TypeError: must be string, not int
    >>> next_monday('2013/01/01')    #doctest: +ELLIPSIS
    Traceback (most recent call last):
    ...
    ValueError: ...
    '''
    return retrieve_date(init_date=date, weekday=0)

def retrieve_date(init_date=None, weekday=None, **param):
    from datetime import date, datetime, timedelta
    if init_date is None:
        init_date = date.today()
    else:
        init_date = datetime.strptime(init_date, "%Y%m%d").date()
    date = init_date + timedelta(**param)
    if weekday is not None:
        d = weekday - date.weekday()
        date += timedelta(d if d > 0 else 7 + d)
    return  date
syntax2html

簡単に解説すると、 last_friday と next_monday の2つの関数があり、それぞれ 指定日の一番最後の金曜日と、次の 月曜日の datetime.date オブジェクトを返します。既に doctest のコードは、この2つの関数には設定されています。

このコードの dcotest を実行するには、どうしたら良いでしょうか?。というのは web2pyの管理画面にはコントローラにあるようなテストボタンが、モジュールにはありません。また、コマンドラインからも実行できないようです。

モジュールテストの実行

doctest はモジュールのままでは実行出来ません。このため、テストが実行可能なコントローラにインポートします。

まず、ダミーのコントローラファイルを作成します。ここでは test.py とし、次のコードを記述します。

1
2
3
4
# coding: utf8
# try something like
from date_calculator import last_friday
from date_calculator import next_monday
syntax2html

コードを記述し保存したら、管理画面からテストを実行してみます。

正常に、 doctest を実行したことが確認できました。

モジュールの doctest ではコントローラにダミーファイルを作成し、モジュールの関数をインポートすることにより実行できるということが、理解して頂けたと思います。