はじめに
Streamlitとは、非常に簡単にWebアプリケーションを作成できるPythonライブラリです。データのアップロードやグラフの可視化の仕組みを素早く実装可能なため、素早い仮説/検証のサイクルが重要となるデータ分析業務やAI等のPoC業務で利用している方も多いのではないでしょうか。
一方、分析基盤としてアプリケーションの規模や複雑さが増してきた場合、テストを通してアプリケーションの処理の妥当性を担保することが重要になることもあると思われます。
本稿では、我々のチームが行ったStreamlitを用いたWebアプリケーションの自動統合テストの基本的な流れをご紹介し、その実装において感じた拡張面における1つのポイントをお伝えできればと思います。
テストの流れ
ご紹介する方法では、pytestというPythonライブラリにより自動テストを実行し、その自動テストの中でStreamlitのAppTestというモジュールを利用してUI操作をシミュレート、アプリケーションが所望の挙動をしているかを確認していくという流れをとります。
まずは簡単な自動テスト例を示します。例にはコメントがあるので、処理自体の内容が理解できるでしょう。その後、それらの処理内容の流れを理解するための補足を記述します。
例
以下のようなStreamlitのスクリプト、app.pyを用意しました。コメントに記述している通り、画面上のチェックボックスにチェックを入れると"Checked"という文字列が画面に表示されるシンプルなものです。
app.py
import streamlit as st
def run() -> None:
## チェックボックスを表示
checkbox = st.checkbox("test")
## チェックが付いたときのみ文字列を表示
if checkbox:
st.write("Checked")
if __name__ == "__main__":
run()
また、app.pyをテストするためのスクリプトとして、次のtest_app.pyを用意しました。
テスト内容としては、以下の3点を確認しています。
- チェックボックスがチェックされていない場合は、文字列が表示されていないか。
- チェックボックスがチェックされている場合は、文字列が表示されているか。
- 表示される文字列は"Checked"と正しいものであるか。
test_app.py
import pytest
from streamlit.testing.v1 import AppTest
def test_checkbox() -> None:
expect = "Checked"
## streamlitアプリのファイルを読み込んで実行
at = AppTest.from_file("app.py")
at.run()
## 画面上に文字列がないことを確認
assert len(at.markdown) == 0
## checkboxのチェックをシミュレート
at.checkbox[0].set_value(True)
## 画面を更新
at.run()
## 画面上に文字列があり、かつ文字列が"Checked"であることを確認
assert len(at.markdown) == 1 and at.markdown[0].value == expect
以上を準備した上で
pytest test_app.py -v
を実行すると、テストが実行されて次のような標準出力がなされます。
============================================================================= test session starts =============================================================================
platform win32 -- Python 3.13.7, pytest-9.0.0, pluggy-1.6.0 -- C:\xxx\.venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: C:\xxx
collected 1 item
test_app.py::test_checkbox PASSED [100%]
============================================================================== 1 passed in 1.07s ==============================================================================
補足
まず、pytestは指定したPythonファイル内の名前が"test"で始まる関数や、指定したディレクトリ内のPythonファイル内の名前が"test"で始まる関数をテストとして認識して実行します。したがって、先ほどのようにpytestを実行するとtest_app.py内のtest_checkbox関数をテストとして実行しています。
test_checkbox関数内では、まずAppTestというテスト用のStreamlitのモジュールを生成して、チェックボックスの挙動をシミュレート、都度所望の挙動か (チェックボックスのチェック有/無と画面上の文字列有/無が一致しているか、また文字列が適切か) をassertによりチェックしています。
assertの結果が全て真のため、最終的な出力にはテストに通ったことが表示されています。注意点としては2つあります。
- “at.run()”によりシミュレートごとにアプリを更新する必要がある点。
- 画面上のウィジェットは配列で格納されている点 (したがって、画面上の文字列有無は“at.markdown”の長さで判定可能)。
テスト実装で感じたポイント
テストを実装していく中で感じた重要なポイントとして「ウィジェットのkey属性を設定する」というものがあります。
以下では、keyとは何か、またなぜ設定した方がよいのかについて説明します。
まずkeyとはウィジェットごとに付与できる属性になっています。上記app.pyの例では、
checkbox = st.checkbox("test")
というようにチェックボックスを作成していました。このとき引数に任意の文字列を与えることでkeyを設定することが可能です。
checkbox = st.checkbox("test", key="check_1")
なぜウィジェットにkeyを設定するとよいのか、というと、自動テストがUIの改変に対してロバストになるためです。
上記のtest_app.pyの例では、チェックボックスのチェックをシミュレートする際、対象をウィジェットの配列のインデックスで指定していました。
at.checkbox[0].set_value(True)
しかし、このテスト対象の上にチェックボックスを追加してしまうと、テスト対象のチェックボックスのインデックスは1になってしまいます。したがって、ウィジェットの追加に応じて自動テストのスクリプトを修正する必要があります。
一方、ウィジェットにkeyを設定すると、シミュレート対象のウィジェットをインデックスではなくkeyで指定することができるようになります。例えば、上記のようにチェックボックスに"check_1"というkeyを設定すると、
at.checkbox(key="check_1").set_value(True)
というようにすることでシミュレートが可能になります。
以上のようにStreamlitでは初めからウィジェットにkeyを設定して、後から改変の必要がない自動テストを組むことが可能となります。
最後に
本稿ではStreamlitで作成したアプリケーションの簡単な自動テスト方法についてと、実装を通じて感じたポイントについてご紹介しました。参考になる点がありましたら幸いです。
私たちのサービスでは、データ分析基盤の構築やDeep Learningモデル開発、MLOps構築、生成AIモデル開発等データに関わるプロジェクトを伴走支援しております。データ分析基盤開発やデータのAI活用経験のある方や、興味のある方は、ぜひご応募ください。あなたのスキルと情熱をお待ちしています。
新卒、キャリア募集しています!

