[Web] Streamlitの簡易的なテストを作成した話
作成したサイト
レポジトリや作成した経緯は以下をご参照ください.
URL
Mahjong Util
Git Hub
https://github.com/ShunDeveloper/streamlit_app
作成した経緯
https://zenn.dev/shundeveloper/articles/f003e73658ce6f
Streamlitとは
StreamlitというPythonフレームワークを御存じでしょうか? Streamlitを使うと美しい表やグラフを使ったアプリケーションを簡単に構築できます. 私自身もユーザーとして利用
させていただいています. 興味を持たれた方は是非公式ドキュメントを確認して見てください.
今回はそんなStreamlitのアプリケーションにテストを追加してみた話です.
方針
公式の推奨する方法があればよいのですが, Documentを見たところ, 特に紹介されていませんでした. Googleで調べてみたところ, SeleniumBaseを使うと良いとのアドバイスがありましたが, 個人開発レベルの小さなアプリケーションにセットアップにコストのかかるSeleniumを使いたくなかったので以下の方針でテストをすることにしました.
- 自作パッケージはPytestで単体でのテストを行う
- アプリケーションはバックグランドで走らせてHTTPリクエストを投げて正しいリクエストを返すかテストをする
Pytestで自作パッケージの挙動をテスト
このレポジトリでは /page
以下で同じ処理を行うことがあり, それらの処理を /utls
(以下 utlsパッケージ)で管理しています. この部分に関してはPytestコマンドでテストすることにしました. Pytestは pytest -s
とコマンドを打つことでPython Scriptのprintをconsoleに出力することができるので書いています.
※4行目のsysの部分ですが, 上位階層のパッケージをimportする為に行っています.
import sys
import os
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
from utils.is_na import is_na
from utils.question import question
def test_is_na_false():
print()
print('FUNCTION WORKING TEST: is_na (output must be false)')
assert is_na(text="1000") == False
print('='*30)
...
HTTPリクエストのステータスコードをチェック
先ほどのテストで自作パッケージのテストはできましたが, アプリケーションが動いているかの確認が出来ていません. そこでShell Scriptを使ってアプリケーションをbackgroudで走らせてそのうえでrequestメソッドを使ってHTTPリクエストを投げてみます.
echo 'start: running app [1/3]'
nohup streamlit run app.py > log.txt 2>&1 &
echo $! > save_pid.txt # save current pid
echo 'ok: running app [1/3]'
echo 'start: testing [2/3]'
pytest -s
echo 'ok: testing [2/3]'
echo 'start: shutdown [3/3]'
kill -9 `cat save_pid.txt`
rm save_pid.txt
echo 'ok: shutdown [3/3]'
[1/3]では, nohupコマンドでstreamlit run app.pyをbackgroud実行しています. これを行うことで[2/3]のコマンドを打つときもアプリケーションの実行がされている状態になります.
また, 3行目の $! > save_pid.txt
では直近の実行されたプロセスのidをテキストファイルに保存しています. これはバックグランド実行したプロセスを kill
するのにプロセスidが必要になるからです.
[2/3]では, pytestでアプリケーションにHTTPリクエストを投げています. 具体的には以下の処理を行っています.
import os
import requests
import time
import re
ROOT_URL = 'http://localhost:8501'
PAGE_PATH = "pages/"
PAGE_NAMES = []
tmp = os.listdir(PAGE_PATH) # get file name
assert 0 < len(tmp) # confirm
for i in range(len(tmp)):
file_name = tmp[i]
file_name = re.sub(r'[0-9]+_', '', file_name)
file_name = re.sub(r'.py', '', file_name)
PAGE_NAMES.append(file_name)
def test_app_root():
print()
print('WORKING TEST: root page')
time.sleep(2)
res = requests.get(ROOT_URL)
res = str(res)
assert '<Response [200]>' == res
print('='*30)
def test_app_page():
print('WORKING TEST: page under pages dir')
for page_name in PAGE_NAMES:
print('page name:', page_name)
time.sleep(2)
res = requests.get(ROOT_URL+'/'+page_name)
res = str(res)
assert '<Response [200]>' == res
print('='*30)
def test_app_page():
print('WORKING TEST: Not Found Request')
time.sleep(2)
res = requests.get(ROOT_URL+'/hoge')
res = str(res)
assert '<Response [404]>' == res
print('='*30)
変数 PAGE_NAMES
にはURLが保存されます. URLは /page
以下のファイル名から「数字_」と「.py」を抜いた文字に設定されるので正規表現で取得しています.
準備が出来たらrequestsメソッドでリクエストを投げてステータスコードを確認しています.
GitHub Actionsで自動実行
以下のURLを参照し, ymlファイルを作成しました. ローカルで使っていたPythonの環境を作成して前述したShell Scriptを動かします.
run-name: test-app
on:
push:
branches:
- dev
pull_request:
branches:
- dev
- main
jobs:
run-python:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.8'
cache: 'pip'
- run: python -m pip install --upgrade pip
- run: pip install -r requirements.txt
- run: bash test/test.sh
コードに関しては以下のページを参考にしました. 詳しく知りたい方は以下のページを参照ください.
Building and testing Python - GitHub Docs
https://github.com/actions/setup-python
おわりに
ご意見ご感想等ありましたらTwitterかzennまでいただけると幸いです.
Discussion