本ドキュメントは、GRDM 結合試験の機械化について、アーキテクチャおよび利用方法について記載しています。
本システムは、現在、人手で実施している GRDM 結合試験において、現行の手順書と同等の内容を機械的に実施できる環境を整備することを目的としています。
E2Eテスト(Playwrightなど)は強力なテストツールですが、UIの構造に依存する部分が多く、予期しない形でテストが壊れることがあります。テストが失敗した際、どこで失敗しているかを正確に把握するのが困難で、UIの小さな変更でもテストコードの修正が必要になることが多く、保守コストが高くなりがちです。
Jupyter Notebookを使うことで、この問題を解決します。セルごとに実行できるため、Step-by-Stepで実行しながら問題箇所を特定できます。各ステップでスクリーンショットを確認しながら進められるため、視覚的にテストの進行状況を把握できます。テストが失敗した場合でも、その直前のセルまでの実行結果が残っているため、問題の切り分けが容易です。インタラクティブな環境で試行錯誤しながらテストコードを修正できるため、保守作業が効率的に行えます。
ソフトウェア開発では、新機能や修正が正しく動作することを確認する「受け入れテスト」と、コード変更の度に自動的に実行される「継続的テスト(CI/CD)」の両方が品質保証に不可欠です。特にOSS開発や複数ベンダが協業する環境では、異なる開発者による変更が既存機能を破壊していないことを継続的に検証するCI/CDの仕組みが、コードベースの品質維持に極めて重要な役割を果たします。 しかし従来、これらは別々のツールやコードで実装されることが多く、テスト内容を異なる形式で二重に管理する必要がありました。この機械化された結合試験の革新的な点は、Jupyter Notebookで記述した一つのテストコードを、開発者が手動で確認する際にはインタラクティブに実行でき、同じものをGitHub Actionsなどの自動化環境でもそのまま実行できることです。つまり、開発時に動作確認したテストが、そのまま今後自動実行される品質チェックとして機能するのです。
本システムは、主に以下の2つの局面で活用されます。
機能開発やバグ修正後の動作確認において、Jupyter Notebookのインタラクティブな特性を最大限に活用します。開発者やテスターは以下のような利点を享受できます:
- ステップバイステップ実行: セルごとに実行して問題箇所を即座に特定
- 視覚的な確認: 各ステップでスクリーンショットを確認しながら進行
- 外部システムとの連携: Googleログインなど、外部のシステムに対する操作を含むテストも実現可能
この場面での利用方法は 結合試験環境のアーキテクチャ セクション以降で詳しく説明します。
GitHub ActionsなどのCI/CDパイプラインに組み込み、papermillによるコマンドライン実行を活用した継続的な品質保証を実現します:
- 自動実行: pushやPull Request時に自動的にテストを実行
- バッチ処理: papermillを使用してNotebookを非対話的に実行
- 並列実行: matrix戦略により複数のテストグループを並列で実行
- 結果の可視化: 実行結果をArtifactsとして保存し、動画やNotebookで確認可能
この場面での利用方法は CI/CD (GitHub Actions) セクションで詳しく説明します。
なお、インタラクティブな入力を必要とするNotebookの一部はCI環境では実行できない場合がありますが、そのような場合は以下の対策を取ります:
- パラメータ化により外部から値を注入
- 条件分岐によるスキップ処理の実装
- CI専用の代替処理の用意(例:FakeCASを使用したモック認証)
すべての受け入れテストをそのままCI/CDに組み込むことは困難な場合もありますが、本システムにより多くの試験を継続的テストに組み込むことが可能になります。これにより:
- 回帰テストの自動化: 既存機能が新しい変更によって壊れていないことを継続的に確認
- 早期問題発見: 問題を開発サイクルの早い段階で発見し、修正コストを削減
- 開発効率の向上: 手動テストの工数を削減し、より創造的な作業に集中
このように、受け入れテストとCI/CDの両面から品質確保に貢献し、GRDMコードベースの安定性と信頼性の向上に寄与します。
このリポジトリではGitHub Actionsを使用してE2Eテストを自動実行しています。テストはpushやpull request時に自動的に実行され、GRDMコードベースの動作を継続的に検証します。 現在はこのリポジトリでの実行のみですが、RDM-osf.ioコードベース側でもE2Eテストを実行することで、継続的な回帰テストの実現を目指しています。
マイグレーションテストは、GRDMのバージョンアップ前後でデータと機能が正しく動作することを確認するテストです。
- マイグレーション前: 旧バージョンでテストデータを作成(例:
取りまとめ-Migration前-20250906.ipynb
) - マイグレーション実行: システムを新バージョンへアップグレード
- マイグレーション後: 新バージョンでデータの整合性と機能を確認(例:
取りまとめ-Migration後-20250906.ipynb
)
GitHub Actionsのワークフローでは、matrix設定でmigration_from
を指定することで実行されます。マイグレーションテスト用のNotebookはmigrations/
ディレクトリに配置されています。
マイグレーション前後で確認したい処理を追加する場合は、migrations/
ディレクトリ内の対応する取りまとめNotebookを編集し、必要に応じて新しいテスト手順Notebookを作成してください。
異なるバージョンからのマイグレーションテストを追加する場合は、.github/workflows/e2e-test.yml
のmatrix設定に新しいエントリを追加し、対応する日付の取りまとめNotebook(取りまとめ-Migration前-YYYYMMDD.ipynb
と取りまとめ-Migration後-YYYYMMDD.ipynb
)をmigrations/
ディレクトリに作成してください。
テスト実行後、結果はGitHub ActionsのArtifactsから確認できます:
-
test-results-failed - 失敗したテストのNotebookのみを含むアーカイブ
- エラーが発生したNotebook(.ipynb)ファイル
- 関連するサブNotebookや実行結果
- 問題の調査に必要な最小限のファイルセット
-
test-results-full - すべてのテスト結果を含む完全なアーカイブ
- 実行されたすべてのNotebook(.ipynb)ファイル
- スクリーンキャプチャ動画(.webm形式)
- 各テストステップの実行結果と証跡
各テストの実行過程は動画(.webm形式)として記録されています:
- 動画はブラウザで直接再生可能
- テストの各ステップがどのように実行されたかを視覚的に確認できる
- エラー発生時の画面状態を正確に把握できる
テスト結果のNotebookファイルには以下の情報が含まれています:
- 各セルの実行結果
- エラーメッセージとスタックトレース
- 実行時のパラメータ
- スクリーンショット(エラー発生時)
これらのNotebookはJupyter環境で開くことで、エラーの詳細を確認し、必要に応じて再実行やデバッグが可能です。
GRDM結合試験の機械化には、以下のソフトウェアを利用します。
- OperationHub https://github.com/NII-cloud-operation/OperationHub
- papermill https://github.com/nteract/papermill
- Jenkins https://www.jenkins.io/
OperationHubは、インフラ運用向けにカスタマイズされたJupyterHubであり、結合試験用に作成されるJupyter Notebookの開発・実行環境を提供します。 papermillは、Jupyter Notebookをプログラムから機械的に実行するためのライブラリです。実行時にパラメータを与えることでJupyter Notebookの動作をカスタマイズして実行することが可能です。papermillのパラメータ機能を用いることで、結合試験のターゲットに関する接続情報や、ストレージに関する認証情報などを外部から与えることができます。 Jenkinsは、OperationHubにて作成されたJupyter Notebookを定期的に実行するためのCI/CDツールです。
本システムでは、テスト実施者は以下のようにして結合試験を行うことができます。
- 試験項目の開発・保守: 試験項目を開発・保守する際は、OperationHub上で結合試験用Jupyter Notebookを作成する。Jupyter NotebookのWeb UIから試験用のスクリプト(ブラウザ上に表示されている要素を取得して表示や動作を検証する)を作成します。Jupyter NotebookのUIを通じて試行錯誤的にスクリプト開発を行うことができます。実行結果に関して、応答性能を収集したり、証跡を記録したりといった処理を記述することも可能です。
- 試験項目の実行: 試験項目を実行するエンジニアは、試験実施Jupyter Notebookを実行する。papermillは、指定されたパラメータとともにJupyter Notebookを実行します。
試験の実施、成功・失敗の判定と、証跡の収集はJupyter Notebookが行います。Jupyter Notebook環境にはpandas, numpy等のデータ処理ライブラリがインストールされているため、テスト結果の分析や可視化も容易に行えます。
結合試験の機械化にあたり、試験単位の設計を行いました。GRDMは主に研究者に対して研究データを管理するためのストレージサービスを提供することが主眼であり、試験項目はストレージに対するデータの登録や読み出しといった操作が中心となります。また、そのデータが格納されたプロジェクトに対して誰がアクセスできるかといったアクセス範囲を制御する機能も重要な試験項目となります。
したがって、主要なテストの観点を以下のように分類しました。
- アクセス主体 ... 誰がデータにアクセスするかを規定します。プロジェクトのオーナーや、読み込みのみのメンバー、読み書き可能なメンバーと言った種類を規定します。
- アクセス方法 ... ストレージサービスの種別やその認証情報に加え、API経由、Web UI経由といったアクセス方法を規定します。
- 操作 ... ストレージに対するデータの登録、読み出し、削除といった、試験対象の機能を規定します。
試験は操作に対して定義するものとし、アクセス主体とアクセス方法はパラメータとして与えられるようにすることを基本方針とします。例えば、「オーナーによるファイル操作」や「非オーナーによるファイル操作」といった形で個別に試験項目を分けることはせず、「ファイル操作(アクセス主体, チェック項目)」といった形で試験項目をパラメータ化して考えることでその種類を最小限に抑え、保守性を高めるためです。
パラメータ化の例としては以下のようになります。
- ファイルアップロード・ダウンロードの試験(ストレージ種別, アクセス主体(オーナー/書き込み権あり非オーナー))
操作は互いに依存する場合があります。例えば、「ファイルアップロード・ダウンロードの試験(標準ストレージ)」は独立して実行することができますが、Amazon S3等の外部ストレージに対する試験を行う場合は、事前にストレージの認証情報を登録しておく必要があります。しかしながら、認証情報の設定方法はストレージサービスの種類ごとに異なるため、これは別々の操作として扱うこととします。例えば、Amazon S3に対する試験の構成は、以下の2つの操作から構成されます。
- Amazon S3の認証情報登録・削除試験
- ファイルアップロード・ダウンロード試験(Amazon S3)
ファイルアップロード・ダウンロード試験は、Amazon S3の認証情報が登録された時点で実行する必要があります。また、ファイルアップロード・ダウンロード試験実施後は、Amazon S3の認証情報を削除する試験も実行する必要があります。そのため、Amazon S3の認証情報特録・削除試験から、ファイルアップロード・ダウンロード試験を呼び出すように設計します。どの試験を呼び出すかは、papermillのパラメータ指定機能を用いて、呼び出すJupyter Notebook名を指定することで実現します。これにより、試験の実行順序や依存関係を柔軟に管理することができます。
互いに依存しない試験項目は独立して、並列に実行できるように設計します。並列実行可能にする配慮としては、以下のようなものがあります。
- 試験で使用するリソース (プロジェクト、ストレージ等) を試験ごとに払い出す
- 試験終了後には、払い出したリソースを解放する
プロジェクトやストレージを試験ごとに共有してしまうと、試験の実施順序により 試験結果が変わってしまう可能性があります。 これらの配慮により、試験環境の競合を避け、試験の独立性を保つことができます。
結合試験はJupyter Notebookの自動実行により実現します。Jupyter Notebookは大きく以下の2つのカテゴリに分類されます。
- テスト手順 Jupyter Notebook - 例:
テスト手順-ストレージ共通.ipynb
- 結合試験実行・取りまとめ Jupyter Notebook - 例:
結合試験-実行.ipynb
テスト手順 Jupyter Notebookは、定点監視時のテスト手順を記載したJupyter Notebookです。Playwright https://playwright.dev/ を用いたHeadlessブラウザ操作を行うためのコードが記載されています。 このNotebookは、終了時に動画キャプチャと通信内容を記録したharファイル、エラー終了した場合は最後のキャプチャ画像を、所定のディレクトリに保存します。 結合試験実行・取りまとめ Jupyter Notebookは、テスト手順 Jupyter Notebookを実行し、結果を取りまとめるJupyter Notebookです。 このNotebookはテスト手順Notebookを実行し、その結果をサマリとしてまとめ、証跡としてファイルに保存します。
新しくJupyter Notebookを追加する際にはファイル名を「テスト手順-」か「取りまとめ-」から始めることで、1.と2.、どちらの種類であるかわかるようにしてください。結合試験-実行.ipynb
はこのルールの例外です。結合試験-実行.ipynb
は結果を収集してサマリにまとめる際に、このファイル名の構造を利用しています。
テスト手順 Jupyter Notebookは、以下のような構成になっています。
- パラメータの設定
- テスト手順の記述
パラメータの設定は、papermillの規約に従い、parameters
タグが付与されたセルに記述します。
テスト手順-一般ユーザーログイン〜ファイル操作-定点監視用.ipynb
では、先頭セルに以下のように記述されています。
rdm_url = 'https://rdm.nii.ac.jp/'
idp_name = 'GakuNin RDM IdP'
idp_username = None
idp_password = None
default_result_path = None
project_name = 'テスト用プロジェクト'
close_on_fail = False
これらの変数は接続先のURL、ログイン情報、結果の保存先、プロジェクト名、エラー発生時の挙動を制御するための変数です。 上記に示したように、これらの多くは空ですが、呼び出し元となるテスト実行・取りまとめ Jupyter Notebookからpapermillにより値が上書きされます。
papermillからではなく、手作業でテスト手順 Jupyter Notebookを作成する場合に備えて、設定によっては値が空の場合にユーザーから入力を求めるようにしています。
if idp_username is None:
idp_username = input(prompt=f'Username for {idp_name}')
if idp_password is None:
idp_password = getpass(prompt=f'Password for {idp_username}@{idp_name}')
(len(idp_username), len(idp_password))
上記のコードは、idp_username
および idp_password
が空の場合に、ユーザーに対してその内容の入力を求めるコードです。
papermillによるコマンドライン実行時には、このような入力を求めるコードは実行することができません。そのため、papermillからこれらの変数の値を指定する必要があります。
テスト手順の記述は、PlaywrightのAPIを用いてブラウザの操作を行うコードが記述されます。
セル実行時に出力結果にキャプチャ画像を貼り付けたり、エラー発生時に必要な情報を取得するためのユーティリティスクリプトを用意しています。
これは scripts/
ディレクトリに格納されています。
このユーティリティスクリプト scripts/playwright.py
を使う場合は、以下のように初期化を行います。
import importlib
import pandas as pd
import scripts.playwright
importlib.reload(scripts.playwright)
from scripts.playwright import *
from scripts import grdm
await init_pw_context(close_on_fail=close_on_fail, last_path=default_result_path)
このコードは、Playwrightの初期化を行い、結果の保存先、エラー発生時の挙動を設定します。
close_on_fail
を True
にすると、エラー発生時に関連するリソースをすべて破棄し、動画キャプチャやharファイルの保存を行います。
False
にすると、画面キャプチャを撮るだけで、リソースは維持します。
Jupyter Notebookを作成する場合は、 close_on_fail
を False
にしておくと、その後の操作を継続的に行うことができます。
papermillの自動実行時は True
にしておくと、エラー発生時にリソースを解放し、関連する動画キャプチャやharファイルなどのデータを引き揚げることができます。
default_result_path
には、結果の保存先を指定します。 None
の場合は、 ~/last-screenshots
に保存されます。
テスト手順の記述は、以下のような形式で記述します。
async def _step(page):
await page.goto(rdm_url)
# 同意する をクリック
await page.locator('//button[text() = "同意する"]').click()
# 同意する が表示されなくなったことを確認
await expect(page.locator('//button[text() = "同意する"]')).to_have_count(0, timeout=500)
await run_pw(_step)
run_pw
関数を利用することで、指定された手順を実行することができます。
run_pw
はユーティリティスクリプトが提供する関数で、Playwrightのコンテキストを初期化し、指定された手順を実行します。
完了時にはスクリーンキャプチャを戻り値として返すため、画面の様子を確認することができます。
run_pw
に与える関数 _step
は、Playwrightの page
オブジェクトを引数に取り、その中でブラウザの操作を行います。
この関数の中では、大きく分けて以下のような操作を行います。
- ページでの操作
- 操作後の状態の確認
ページでの操作は、PlaywrightのAPIを用いて行います。例えば、 page.goto
で指定されたURLに遷移し、 page.locator
で指定された要素に対して click
を呼び出し、クリックを実行することができます。
https://playwright.dev/python/docs/pages に記載されているAPIを利用することで、ブラウザの操作を自動化することができます。
操作後の状態の確認は、 expect
関数を用いて行います。 expect
関数は、指定された条件が満たされるまで待機し、条件が満たされた場合に次の処理を行います。
https://playwright.dev/python/docs/test-assertions に記載されているAPIを利用することで、ページ上の要素の状態を確認することができます。
timeout
で指定された時間内に条件が満たされない場合は、エラーとなります。
テスト手順の記述部分は典型的には以下のようにセルが並びます。
- レベル1の見出しを1行目とするMarkdownセル
- レベル2の見出しを2行目とするMarkdownセル
_step
関数を定義し、ユーティリティrun_pw
によってそれを実行するセル- (2.と3.を交互に繰り返す)
- 終了処理
実際には、1.から4.をひとかたまりとして、これを複数個並べても構いません。
このひとかたまりをStepSequence、1.をStepSequence Headerと呼びます。同様
に、2.と3.のひとかたまりをStep、2.をStep Headerと呼びます。3.には複数の
セルを配置しても構いません。例えば、上で述べたinit_pw_context
による初
期化セルなどはその実例です。
StepSequence Headerの2行目以降に以下の内容を含めることが推奨されます。
- サブシステム名: {}
- ページ/アドオン: {}
- 機能分類: {}
- シナリオ名: {}
- 用意するテストデータ: {}
ただし、{}
はプレースホルダで、そのStepSequenceに沿った内容を記述してく
ださい。見出しの内容も含め、これらはテスト結果をまとめたxlsxファイルに
反映されるので、適切に記述しておくこと結果を理解しやすくなると期待されま
す。
Step Headerの見出しの内容、そして2行目以降の内容も、やはりxlsxファイルに 反映されるので、必要に応じて2行目以降にそのStepの説明を記述しておくと理 解を助けます。
StepSequence Header、Step Headerどちらについても、1行目の見出し行は#
に
よる表現を利用して記述してください。
1つのStepで2回以上run_pw
を呼び出すことは推奨されません。形式的には許容
されますが、run_pw
がスクリーンショットのうち、そのStepで最後に生成され
たものしか結果ディレクトリに保存されません。
操作対象として要素を特定する場合は、page.locator()
を利用します。page.locator()
は、CSSセレクタやXPathを用いて要素を特定することができます。要素を特定するためのセレクタは、以下のような記述を行うことを推奨します。
- id属性を指定する ... 要素に指定された id属性は、原則として一意であるため、GUIの変更に強い記述をすることができます。
- data-test属性を指定する ... GRDMで作成されるGUIの一部には、要素に
data-test
属性を付与することで、テストコードから要素を特定するための属性であることが明示されているものがあります。 - テキストを指定する ... ボタンに表示されたテキストを指定することができます。この方法の場合、多言語対応が必要になった場合に修正箇所が多くなる可能性があります。
- 複数の要素の組み合わせで指定する ... 対象の要素に上記の属性がない場合、複数の要素を組み合わせて特定することができます。例えば、フォルダアイコンに隣接する特定のテキストを指定する、といった方法が考えられます。
XPathやCSSセレクタの例については、Jupyter Notebookを参考にしてください。 既存のGUIの変更があった場合に、これらの記述の修正が必要になる場合があります。要素が見つからないといったエラーが発生した場合は、直前スクリーンショットを参照し、画面を特定した上で、ブラウザの 開発者ツールを用いて要素を特定し、セレクタを修正する必要があります。
なお、ブラウザの開発者ツールを用いて確認することができますが、 //*[@id="tb-tbody"]/div/div/div[11]/div[1]/span[2]/span のように、要素や階層や順序がXPath内部に含まれてしまうため、GUIの変更に追従して修正する必要が生じるため、推奨されません。
GRDMには、一般的なGUI操作を行うためのユーティリティ関数群が用意されています。 scripts/grdm.py に定義されています。
- expect_idp_login ... IdPへのログインを実施可能な状態となるまで待ちます。ID/PWの入力フォームが表示されるまで待機します。
- login_idp ... IdPにログインする。ID/PWを入力してログインします。
- ensure_project_exists ... 指定された名前のプロジェクトが存在しない場合、プロジェクトを作成します。
- delete_project ... 指定されたプロジェクトを削除します。
- get_select_storage_title_locator, get_select_storage_title_xpath ... 「NII Storage」等、ストレージ名を示す要素を特定するための関数です。
- get_select_expanded_storage_title_locator, get_select_expanded_storage_title_xpath ... 展開された状態のストレージ名を示す要素を特定するための関数です。ストレージの内容がロードされるまで待機する場合に利用します。
- get_select_folder_title_locator, get_select_folder_title_xpath, get_select_folder_toggle_locator, get_select_folder_toggle_xpath ... フォルダ名を示す要素を特定するための関数です。title は、フォルダ名のテキスト、toggle は、フォルダの展開・折りたたみアイコンを示す要素を特定するための関数です。
- get_select_file_title_locator, get_select_file_title_xpath ... ファイル名を示す要素を特定するための関数です。
- get_select_file_extension_locator, get_select_file_extension_xpath ... ファイルの種別を示すアイコン要素を示す要素を特定するための関数です。
- wait_for_uploaded ... ファイルがアップロードされるまで待機するための関数です。ファイルのプログレスバーが表示されている間待機します。
- upload_file, drop_file ... ファイルをアップロードするための関数です。upload_fileはストレージやフォルダ選択時に現れる「アップロード」ボタンを使い、drop_fileはファイルを画面にドロップしてアップロードします。drop_fileはファイルをJavaScriptに変換して実行するため、数MB程度までのファイルにのみ利用可能です。
結合試験実行・取りまとめ Jupyter Notebookは、以下のような構成になっています。
- パラメータの設定
- テスト手順 Jupyter Notebookの実行
- 結果の取りまとめ
最初の パラメータの設定 では、対象となる結合試験の実行に必要な変数を設定します。この情報は、実行されるNotebookに渡される引数として使用されます。例えば、システムのURLやユーザー認証情報などが含まれます。 次に、 テスト手順 Jupyter Notebookの実行 では、papermillを使用して、指定したテスト手順 Jupyter Notebookをパラメータ付きで実行します。この段階で、テスト用のNotebookが処理され、各ステップの結果が生成されます。テストの構成により、実行されたNotebookがさらに別の Notebookを呼び出すこともあります。 結果の取りまとめでは、テストの実行後、得られた結果を収集し、結果を取りまとめます。この取りまとめには、失敗したものがある場合はそのサマリ、性能情報の視覚化が含まれます。
全てのテスト手順Jupyter Notebookの実行結果には、動画でのスクリーンキャプチャを添付することで、状況の確認の助けとします。動画のシーンがテストのどの手順に対応しているかの参考にできるよう、実行中のセルの見出し文字列が字幕として挿入されます。
このリポジトリをGitで管理・公開する際は、機密情報の流出を防ぐためにpre-commit hookを設定してください。
-
必要なツールのインストール
# macOSの場合 brew install gitleaks # pre-commitをインストール pip install pre-commit
-
pre-commit hookの有効化
# リポジトリのルートディレクトリで実行 pre-commit install
-
動作確認
# 全ファイルをチェック pre-commit run --all-files
pre-commit hookは以下の情報を自動的に検出し、コミットをブロックします:
- AWSアクセスキー、シークレットキー
- APIトークン、認証トークン
- プライベートキー
- その他の機密情報パターン
また、Notebookファイルのコミット時には自動的に不要なメタデータ(lc_server_signature.history)が削除されます。
Jupyter Notebookの出力セルには実行時の情報が記録されることがあります。必要に応じてセルの出力を消去してください。