Python.use(better)「テストケースを生成するコード」を生成するコード

記事一覧入門編基礎編応用編中級編

Python.use(better)
「テストケースを生成するコード」を生成する
《Jython2.5.0》

《著》小粒ちゃん+∞《監修》小泉ひよ子とタマゴ倶楽部
第0版♪2001/03/02 ● 第1版♪2003/05/25 ● 第2版♪2004/06/01 ● 第3版♪2009/02/28

■ 概要

♪スタッフ研修用のセミナー教材(2003)に加筆(再構成)して、公式マニュアルの補足情報を提供します。

25.2. doctest — Test interactive Python examples ★★

「《テストケースを生成するコード》を生成するコード」を記述する方法を紹介します。

ここでも、基本的なアプローチとして「共通項を抽出してひとつにまとめる」リファクタリングの定石に従います。前述したテストケース ex_1/ex_2 には、共通する中にいくつかの相違点があります。

def ex(testcase):
    for e in range(testcase+1):
        print ">>> #","-"*20,"ex_%s"%e
        eval("ex_%d()"%e)
        print

まず、テストの対象となるメソッド find/index の違いがあります。次に、例外 ValueError を生成するかどうかの違いがあります。そこで、これらの違いを捨象した、抽象表現に変換します。それには、メタプログラミングの基本ツールとして組み込み関数 eval/compile を利用します。

メソッド名の違いは、実引数を介して実行時に確定させます。例外処理については、ValueError を生成する可能性とは無関係に、つねに try/except ブロックを用意します。

def examples():
    print ">>> #","-"*20,"examples"
    for s1,s2 in [
        ('ABC@', '"ABC".find(%r)'    ),
        ('ABC@', '"ABC".index(%r)'   ),
        ('ABC@', '"ABCABA".count(%r)'),
        ]:
        X = ex_(s1, s2)
        eval(compile(X,"","exec"))

def ex_(s1, s2):
    return '''def ex():
    for e in %r:
        X = '%s'%%e
        print ">>> try:"
        print "...    ",X
        print "... except ValueError,e:"
        print "...     print e"
        try:            
            eval(compile(X,"","single"))
        except ValueError,e:
            print e
ex()'''%(s1,s2)

これを実行すると、次のような出力が得られます。

>>> examples() 
>>> # -------------------- examples
>>> try:
...     "ABC".find('A')
... except ValueError,e:
...     print e
0
>>> try:
...     "ABC".find('B')
... except ValueError,e:
...     print e
1
>>> try:
...     "ABC".find('C')
... except ValueError,e:
...     print e
2
>>> try:
...     "ABC".find('@')
... except ValueError,e:
...     print e
-1
>>> try:
...     "ABC".index('A')
... except ValueError,e:
...     print e
0
>>> try:
...     "ABC".index('B')
... except ValueError,e:
...     print e
1
>>> try:
...     "ABC".index('C')
... except ValueError,e:
...     print e
2
>>> try:
...     "ABC".index('@')
... except ValueError,e:
...     print e
substring not found in string.index
>>> try:
...     "ABCABA".count('A')
... except ValueError,e:
...     print e
3
>>> try:
...     "ABCABA".count('B')
... except ValueError,e:
...     print e
2
>>> try:
...     "ABCABA".count('C')
... except ValueError,e:
...     print e
1
>>> try:
...     "ABCABA".count('@')
... except ValueError,e:
...     print e
0
■ 何が問題か

より汎用的な問題を扱うときには、まだ問題が残ります。たとえば、別の例外を扱うためには、ValueError をコンパイル時に確定させずに、これも実引数を介して実行時に確定させる必要があります。すると「《テストケースを生成するコード》を生成するコード》を生成するコード…」という具合に、欲を出せば切りがありません。

このように、汎用的な問題解決、本格的なメタプログラミングに関する話題は、中級篇で紹介します。初級篇では、メタプログラミングの扉を開く話題に止め、解説を続けます。


》作業中です《






Last updated♪2009/07/26