コンテンツにスキップ

アプリケーションのテスト

このドキュメントでは開発の際に欠かせないアプリケーションのテストについて述べます.bamboo ではローカルでのサーバーのテストのためのいくらかのユーティリティを提供しています.以下では,このユーティリティを利用したテスト方法について説明していきます.

概要

アプリケーションを作成したらまずローカル環境でテストを実行することでしょう.しかし,サーバーのテストは少し厄介です.なぜなら,サーバーとクライアントという少なくとも2つのプロセスが存在するからです.ましてや,マイクロサービスのようなサーバーサイドで複数のサーバーアプリケーションがホスティングしている状態では,1つのサーバーアプリケーションにつきコマンドを叩いて起動していてはデバッグも捗りません.

このような事態を回避するために,bamboo では TestExecutor というクラスが用意されています.このクラスを使用することによって,テスト用の1つのスクリプトを実行することでテストを行うことが出来ます.以下ではこの TestExecutor と Python 標準ライブラリの unittest を用いたテストスクリプトの作成方法について説明します.

TestExecutor と ServerForm

TestExecutor はサーバー起動時に使用するフォームを定義する ServerForm オブジェクトを登録することで,子プロセスとしてサーバーアプリケーションを起動するオブジェクトです.ServerForm は単なる dataclass であり以下のようなインスタンス変数を持ちます:

  • ホスト名 (IP アドレス) hostname
  • ポート番号 port
  • サーバーアプリケーション app
  • ログ出力用のパス path_log

特にサーバーアプリケーションとは bamboo によって実装された App オブジェクトのことです.また,path_log に指定されたログファイルは,サーバーが子プロセスで起動された後にそのプロセスの標準出力,標準エラー出力へ接続されます.

作成したフォームは TestExecutor 生成時に指定できます.以下では app1app2 という2つのサーバーアプリケーションについてテストすると仮定して TestExecutor オブジェクトを生成する例を示しています:

from bamboo import ServerForm, TestExecutor

form1 = ServerForm("localhost", 8000, app1, "test_app1.log")
form2 = ServerForm("localhost", 8001, app2, "test_app2.log")

executor = TestExecutor(form1, form2)

ここまで来れば準備完了です.TestExecutor オブジェクトはコンテキストマネージャでもあり,with 文を使ってブロック内部で定義された処理を行う間のみ子プロセスでフォームに定義されたサーバーアプリケーションを起動することが出来ます.フォームに定義されたサーバーアプリケーションの起動には,TestExecutor.start_serve() メソッドを使います:

with executor.start_serve():
    # クライアントの処理

with 文のブロック内に定義するクライアントの処理はテストしたい処理に依ります.with 文を使用しない場合,start_serve() メソッドによってサーバーアプリケーションを起動し,close() メソッドによって起動したサーバーアプリケーションを終了させることが出来ます.また,クライアントの処理が単一の関数で定義されている場合,TestExecutor.exec() メソッドを使用できます:

def client_test():
    # クライアントの処理

# サーバーアプリケーションを起動しクライアント処理を実行
# クライアント処理の実行後にサーバーアプリケーションを終了
executor.exec(client_test)

テストスクリプトの作成とテストの実行

上述した TestExecutor オブジェクトを利用してテストスクリプトを作成できます.ここでは Python 標準ライブラリである unittest を使用したテストスクリプトの作成方法について説明します.unittest についての説明は行いませんので,その詳細は公式ドキュメントを参照してください.

unittest では unittest.TestCase サブクラスを定義することでユニットテストの1つのケースを定義します.TestCase クラスには setUpClass() クラスメソッドと tearDownClass() クラスメソッドがあり,それぞれテスト開始時,終了時に一度だけ実行されるメソッドです.この2つのメソッドと TestExecutor を利用することで,以下のように unittest のアーキテクチャに即したテストケースを定義できます:

import unittest

class UselessTest(unittest.TestCase):

    @classmethod
    def setUpClass(self):
        # テスト実行前に実行される
        # サーバーを起動
        form1 = ServerForm("localhost", 8000, app1, "test_app1.log")
        form2 = ServerForm("localhost", 8001, app2, "test_app2.log")

        self.executor = TestExecutor(form1, form2)
        self.executor.start_serve()

    @classmethod
    def tearDownClass(self):
        # テスト実行後に実行される
        # サーバーを停止
        self.executor.close()

    # テストメソッドの定義
    # クライアントサイドの処理を記述
    def test_something(self):
        ...

if __name__ == "__main__":
    unittest.main()

このようにテストスクリプトを定義することで unittest の機能をそのまま利用することが出来ます.テストを実行するためには作成したテストスクリプトを実行するだけです.bamboo 開発用のテストスクリプトはほとんどがこの方法で書かれています.bamboo/test ディレクトリ内のテストコードからテストスクリプト作成のヒントが得られるかもしれません.

Back to top