前回まででプログラムを処理ごとにまとめる関数を紹介しました。関数1つ1つは小さくても、それらを基本的な部品として組み合わせることで大きなプログラムを作ることができるようになります。

関数の動作が誤っていると最終的なプログラムの動作も誤ってしまうため、各関数が期待通り動作することを確かめることが重要です。そこで今回は対話システムの作成から少し離れて、関数が期待通りに動作しているかどうかを確認する方法について紹介したいと思います。

前回までは関数を定義した後、いくつかの引数に対して関数を実行し、その戻り値が期待する値であるかどうかを目視で確認していました。ここではこれを「手動テスト」と呼ぶことにしましょう。関数が1つ2つの場合は手動テストでもうまくいくかもしれません。しかし、関数やケースが増えるにつれて手動テストでは確認するのが困難になっていくのは想像に難くありません。

それだけではなく、手動テストは「目視で確認」という人の手が介入することも問題です。というのも、人手による確認作業は、ミスが発生しやすいからです。期待通りの動作をしていないにもかかわらず「きちんと動作している」と判断してしまったり、テストする度にテスト結果が変わってしまったりします。こうなってくると、当初は期待通り動作するかどうかを確認するためにテストをしていたはずが、そもそもテスト結果が正しいのかすら判断できず、だんだんとテストが形骸化していってしまうのです。

今回紹介するテストでは、関数が期待する動作をするか否かを「プログラムでチェック」します。これにより、いつ誰が行っても同じ品質で関数の動作を確認できるようになるのです。こう言うと難しく聞こえてしまうかもしれませんが、今までの目視による関数実行の結果の確認を、代わりにプログラムで確認するだけなので心配はいりません!

それでは早速始めていきましょう。作業ディレクトリは「C:\Users\user\Documents\pychat\function_test」としますので、事前に作成しておいてください。

モジュールを作ろう

一度定義した関数は、いろいろなところで再利用したいものです。

本連載では、今までPythonのプログラムを記述したファイル「Pythonスクリプト」を作成してきました(Pythonスクリプトについて詳しくは連載第3回をご覧ください)。関数定義をPythonスクリプトに書き、そして同じPythonスクリプトのなかで関数実行をしていました。しかし、関数定義と関数実行を同じPythonスクリプトに書いていたのでは、定義した関数をほかのPythonスクリプトで使いたいときに再利用できません。

そこでPythonは、モジュールという仕組みを提供しています。関数やオブジェクトを定義したPythonのプログラム(これを「モジュール」と呼びます)を用意しておけば、別のPythonスクリプトやモジュールから呼び出せるのです。

実はPythonスクリプトもモジュールです。トップレベルで呼び出されるモジュールをPythonスクリプトと呼びます。したがって、モジュールの作り方もPythonスクリプトとほぼ同様なので心配しないでください。

それではモジュールを実際に作ってみましょう。第15回では、文字列textが正規表現のリストpatternsのいずれかにマッチするかどうかを判定する関数「check_match」を作成しました。check_matchは汎用性があって便利なので、この関数を定義したモジュールmatchを作成してみましょう。

モジュールの実体は1つのファイルで、ファイル名を「モジュール名.py」として作成します。今回はmatchモジュールを作成するので、「match.py」というファイル名にします。

モジュールの中には提供したい関数などを実装するので、第15回で実装したcheck_matchの関数定義をそのまま記述します。結果、次のようなモジュールが出来上がります。

# match.py - 文字列マッチの関数を定義するモジュール
import re

# 正規表現のリスト patterns のいずれかに文字列 text がマッチするか判定する
# マッチした場合は True を、そうでない場合は False を返す
def check_match(patterns, text):
    matched = False  # マッチしたかを記録する変数
    for pattern in patterns:
        if re.search(pattern, text):
            matched = True
            break
    return matched

それでは、このモジュールをPythonスクリプトから呼び出してみましょう。呼び出すには、まずPythonスクリプトからモジュールをインポートする必要があります。モジュールのインポートには、次のような形式で「import 」に続いてモジュール名を記述します。

import モジュール名

今回実装したmatchモジュールをimportするなら、以下のように記述することになります。

import match

改めて説明しましたが、本連載の読者である皆さんは既に何度もモジュールのインポートをしたことがありますよね! 実際、先ほど実装した「match.py」モジュールの先頭でも「import re」のように記述して、reモジュールをインポートしています。第7回では、Pythonが標準で提供しているモジュールの使い方を紹介しているので、そちらも参考にしてください。

さて、モジュールをインポートしたらモジュールに定義した関数を呼び出してみましょう。モジュールの名前の後ろに「.(ピリオド)」を付けて指定することで、モジュールで定義した関数を呼び出すことができます。例えば、matchモジュールのcheck_match関数を使うには「match.check_match」のように記述します。

それでは、match.check_match関数を使って、第15回で実装した関数実行を「main.py」というスクリプト名で実装してみましょう。

# main.py - match モジュールを利用したPythonスクリプト
import match

patterns = ["富士山.*高さ", "東京.*区.*いくつ"]
text_fuji = "ねぇ、富士山の高さは?"
matched_fuji = match.check_match(patterns, text_fuji)
print(matched_fuji)

text_tokyo = "東京の区っていくつあるの?"
matched_tokyo = match.check_match(patterns, text_tokyo)
print(matched_tokyo)

実行すると第15回と同様の結果が得られることがわかります。

$ python main.py
True
True

* * *

以上、今回は関数をテストする意義を確認した上で、テスト用のモジュールの作り方を紹介しました。

これまでは、関数定義と関数実行を1つのPythonスクリプトに記述していましたが、関数定義はモジュールに、関数実行はPythonスクリプトに分割しました。このように役割分担したことにより、各プログラムが格段にわかりやすくなったはずです。さらに、モジュールに定義した関数を複数のPythonスクリプトから実行できるようになったため、関数の再利用が可能になりました。

次回は、テストとはそもそも何をすればいいのかについて考察した後、テストに用いる「assert文」を紹介します。

著者紹介


株式会社NTTドコモ
R&Dイノベーション本部 サービスイノベーション部
阿部憲幸

2015年京都大学大学院理学研究科数学・数理解析専攻修了。 同年、NECに入社。 2016年から国立研究開発法人情報通信研究機構出向。 2018年より現職。 自然言語処理、特に対話システムの研究開発に従事。 毎日話したくなるAIを夢見て日夜コーディングに励む。
GitHub:https://github.com/noriyukipy