テストの実行¶
扱うこと: テストの置き場所、タグ(Unit・Io・Meta・Office・Slow・Manual)と実行のしかた、カバレッジの下限。扱わないこと: CI のワークフローそのもの(CI)。先に読むページ: テスト。
flowchart LR
tag["タグ<br>Unit・Io・Meta・Office・Slow・Manual"] --> local["手元<br>.\tests\run.ps1"]
tag --> hook["pre-commit フック<br>run_commit_tests.ps1(対応するテストだけ)"]
tag --> ci["CI(test.yml)<br>run.ps1 -Ci(既定 + カバレッジ)"]
テストは scripts/ と同じ構成に並べる。どのモジュールのテストがどこにあるか、たどらなくても分かるようにするため。
| フォルダ | 内容 |
|---|---|
tests/helpers/ |
共通の準備(load.ps1。$here・${scriptsDir}・${testDataDir} を決めて tebunko/lib.ps1 を読み込む)とテスト用の TSV 作成(tsv.ps1)・一時の git リポジトリの作成とその守り(git_repo.ps1) |
tests/shared/core/ |
fs・text・folder・worker_pool・data_dir |
tests/shared/office/ |
office_process・office_reader・office_embedded・office_app |
tests/shared/ui/ |
画面の型(types) |
tests/tebunko/core/ |
settings(setting.config)・workspace・status_file・source_name |
tests/tebunko/index/ |
index_name・index_store・content_index_format・gram_index・gram_index_store |
tests/tebunko/indexer/ |
indexer_state・indexer_decide・indexer_plan・extract_office・index_migrate・indexing_session、起動口の通しのテスト(indexer) |
tests/tebunko/search/ |
search_query・search_run・content_index_search・search_service・source_map・2-gram の索引で絞る(search_gram・gram_search) |
tests/tebunko/ui/ |
画面の判断層(index\index_*_view・indexing_view・search\*_view・preview_view・settings\settings_view)と、$ui を偽物にした画面の部品(result_list・open_source・preview・index_tree)・型(search\result_types・search\target_types・index\index_types) |
tests/gui/ |
画面のスモークテスト(gui_helpers=共通の関数、smoke・index・search・settings・leftover・leftover_real=場面。タグ Gui。画面のスモークテスト) |
tests/tools/ |
開発用の道具(run_isolated・check_commit_message・check_signoff・check_release_tag・check_markdown_links・measure_perf・run_commit_tests・pr_checks_comment・git_repo) |
tests/meta/ |
構成を守るテスト(structure・encoding・layers・links・runner・classes・test_isolation)と安全性の検査(safety・installer) |
tests/testdata/ |
手動の結合テスト用のデータ(結合テスト(手動))と、その生成(make_testdata.ps1)・個人情報の除去(scrub_personal) |
既定のワークスペースを守る¶
テストと実機の確かめは、利用者の既定のワークスペース(%USERPROFILE%\Documents\tebunko_ws)に書かない。既定のワークスペースは環境変数 TEBUNKO_DEFAULT_WORKSPACE で差し替えられる(getDefaultWorkDir。絶対パスだけ。引数の profileDir を明示したときは使わない)。この変数は子のプロセス(画面・indexer.ps1・単一 .ps1 版)にも引き継がれる。
| 守り | 内容 |
|---|---|
tests/run.ps1 |
Pester の前に使い捨てのフォルダを作って変数に入れる。入れた場所が既定のワークスペースの場所と同じかその下なら、流さずに止まる。流す前後で既定のワークスペースの名前・大きさ・更新時刻(中身は読まない)を比べ、違えば失敗にする(どのタグでも) |
tests/helpers/load.ps1 |
lib.ps1 を読む前に、変数が空なら使い捨てを入れる(Invoke-Pester を直接流したとき)。initWorkspace のあとで、決まったワークスペースが利用者の既定のワークスペースでないことを確かめる |
tests/gui/gui_helpers.ps1 |
画面の起動の前に、差し替えた既定の場所が利用者の既定のワークスペースでないことを確かめ、画面のプロセスにだけ変数を渡す。前後の写しに既定のワークスペースを含める |
runIndexer(tests/helpers/indexer.ps1) |
indexer.ps1 を動かす前に、取り込みの出力先が使い捨ての中であることを確かめる |
tools/capture_screens.ps1 |
撮る前に使い捨てを入れ、撮り終えたあとで既定のワークスペースの前後を比べる |
tests/meta/test_isolation.Tests.ps1 |
上の仕組みが残っていること、initWorkspace や workspaceFolder = "" を差し替えずに使う書き方が決まったファイルの外に無いことを確かめる |
実機で画面や取り込みを確かめるときは tools/run_isolated.ps1 を通す。%TEMP% の下の新しいフォルダに scripts\ と tebunko.bat の写し・setting.config(-Settings で指定)・使い捨てのワークスペースを作り、起動の前に設定ファイルの場所と work の場所が使い捨ての中であることを確かめてから起動する(外を指していれば起動しない)。画面を自動で確かめるときは -WaitSeconds <秒> で待つ秒数を指定する(過ぎても生きていれば、起動した PID とその子だけを止める。待つ前に 0 以外で終わると終了コード 1)。終わったら、既定のワークスペースと、リポジトリの setting.config・work\ の前後を比べ、違いがあれば一覧を出して終了コード 1 にする。共通の関数は tools/isolation/isolation_common.ps1。
前後の比べは、このプロセスの外が既定のワークスペースに書いた場合(利用者自身の操作・別の作業ツリーのテスト)も失敗にする。失敗したときは、一覧(名前・大きさ・更新時刻)の更新時刻を手がかりに、何が書いたかを確かめる。
守りの限界: 既定のワークスペースの場所との同じ・下かどうかは、パスの文字で比べる。8.3 形式の短い名前・ジャンクション・subst で既定のワークスペースを指す場所は、事前の確かめでは見つけられない(流したあとの前後の比べが、書かれたことを見つける)。前後の比べはフォルダの更新時刻を比べないので、比べの間に作られて消えたもの(跡が残らないもの)は見つけられない。また tools/perf/ など run.ps1 や run_isolated.ps1 を通らない道具は、この守りの外にある。
入力と期待値だけが違うテストは、-TestCases の 1 つの It にまとめる(例: tests/tebunko/ui/search/result_types.Tests.ps1 の HitRow.MatchesFileName)。表は It の中にそのまま書き、計算で作らない。キーには input・args・_・Matches など PowerShell の自動変数の名前を使わない。各行に name を持たせ、It "<name>" で失敗した行が分かるようにする。表の中で変数($stateDone など)を使うときは、BeforeDiscovery で用意する(表はテストを探す段階で作られ、BeforeAll より先に評価されるため)。
Pester 5 はテストを「探す段階」と「流す段階」に分けて動かす。探す段階では Describe の中身がそのまま実行され、BeforeAll の中身は流す段階まで実行されない。そのため次のように書く。
- ファイルの先頭の読み込み(
. "$PSScriptRoot\..\helpers\load.ps1"・Add-Type)と、テスト用の関数・偽物のオブジェクトは、いちばん外のBeforeAll { }に書く。 Describeの準備($TestDriveへのファイル作成・変数・Mock)は、そのDescribeのBeforeAll/BeforeEachに書く。探す段階では$TestDriveが空のため、Describeの中に直接書くとドライブ直下を指してしまう。Mockはそれを書いたブロック(It・Describe)の中だけで効く。呼ばれたかはShould -Invokeで確かめる。- テストが自分で起こしたプロセスの後始末は、
tests/helpers/process.ps1のstopStartedProcess(止める)・waitProcessIdExit(待つだけ)を使う。HasExitedを見てからStop-Process・.Kill()を呼ぶ書き方は、自分で終わるプロセスと競合して揺れる。ここ以外のStop-Process・Wait-Process・.Kill()・名前で止める形はtests/meta/test_process_cleanup.Tests.ps1が落とす。
tests/meta/ は、直し忘れると気づきにくい決まりごとを機械的に確かめる。
| テスト | 確かめること |
|---|---|
structure |
$rootDir などのパス定義、全 .ps1 の構文、ui/ のスクリプトで $PSScriptRoot を使わない、gui.ps1 が指すインデクサのファイルがある、XAML が XML として読める・gui.ps1 が使う x:Name がある、型を読み込む順で継承が解決できる |
encoding |
全 .ps1・.xaml が BOM 付き UTF-8 で、改行が CRLF。.github/codecov.yml が ASCII の文字だけ |
layers |
shared/ にツールの名前が出てこない、ツール同士が互いを読み込まない、起動口からたどれない .ps1 が無い、判断層(text.ps1・index_name.ps1・source_name.ps1・search_query.ps1・indexer_decide.ps1・*_view.ps1)に画面への依存が無い |
links |
git で管理している全 .md の相対リンク(画像・参照リンクの定義・HTML の href/src を含む)の先のファイルがあり(大文字・小文字も区別する)、.md のアンカーの見出しがある(tools/check_markdown_links.ps1。外部の URL は調べない) |
runner |
tests/run.ps1 が、実行したテストが 0 件なら失敗にすること、powershell.exe -File で渡したカンマ区切りのタグを分けて受け取ること、Gui を既定では流さず -Tag Gui と -All では流すこと |
safety |
危険な処理を使っていない、Office をマクロ無効・読み取り専用で開く、原本を書き換えない、書き込み先が work 配下だけ(%TEMP% は前の版が残した作業フォルダの片付けだけ。%LOCALAPPDATA%\tebunko には書かない)、PSScriptAnalyzer の指摘が 0 件、審査用の資料がそろっている(単体テスト(検査と道具)、安全性の要約) |
test_process_cleanup |
tests/ の中の Stop-Process・Wait-Process・.Kill()・名前で止める形が、tests/helpers/process.ps1 の外に無い(例外は run_isolated の木ごと止める taskkill /PID … /T の 1 件) |
タグと実行¶
Describe にタグを付け、実行するものを選べるようにする。
| タグ | 内容 | 外部ソフト | 既定で実行 |
|---|---|---|---|
Unit |
ファイルに触らないもの(判断層、画面の部品を偽物にしたもの) | 不要 | する |
Io |
ファイルの読み書き($TestDrive の中で完結する) |
不要 | する |
Meta |
構成を守るテスト・安全性の検査 | 不要(PSScriptAnalyzer があれば静的解析も行う) | する |
Office |
Excel・Word・PowerPoint の COM を実際に動かすもの(indexer.Tests.ps1 の「利用者のPowerPointが起動している場合」。ほかは Mock で確かめる) |
必要 | しない(-All で実行) |
Gui |
本物の画面(scripts/tebunko/gui.ps1)を別のプロセスで開いて UI オートメーションで操作するもの(tests/gui/。画面のスモークテスト) |
不要(Windows の画面が要る。ランナーの Windows で動く) | しない(-Tag Gui・-All で実行。CI は gui.yml) |
Slow |
時間のかかるもの(検索・本文インデックスの作成・取り込みの速さの回帰テスト。tests/tools/perf_*.Tests.ps1。perf-check.yml) |
検索の側は tebunko-perfdata(データを作るスクリプトのリポジトリ)が要る | しない(-All または -Tag Slow で実行) |
Manual |
手で確かめるもの(今は該当するテストが無い) | – | しない(-All でも実行しない) |
実行は tests/run.ps1 から行う。
.\tests\run.ps1 既定(Unit・Io・Meta。Office・Slow・Gui・Manual は外す)
.\tests\run.ps1 -Tag Unit 速い確認だけ
.\tests\run.ps1 -All Office・Slow・Gui も含める(Office と、tebunko-perfdata が必要。Gui は画面を開くので、操作しないで待つ)
.\tests\run.ps1 -Tag Slow Slow だけ(手元で 3〜4 分ずつ。下の「`perf-check.yml`」)
.\tests\run.ps1 -Tag Gui 画面のスモークテストだけ(約 4 分。流している間はマウス・キーボードに触らない)
.\tests\run.ps1 -Tag Gui -Path .\tests\gui\search.Tests.ps1 1 つの場面だけ
.\tests\run.ps1 -Ci 結果の XML(work\test\results.xml)とカバレッジ(work\test\coverage.xml)を出し、カバレッジの下限を確かめる
.\tests\run.ps1 -Path .\tests\shared\core 指定したフォルダ・ファイルのテストだけ
.\tests\run.ps1 -Quiet 失敗したテストだけを表示する
いずれも失敗したテストの数を終了コードにする(フックと CI が見る)。1 件も実行しなかったときも失敗にする(終了コード 1)。タグの打ち間違いで、何も確かめないまま通るのを防ぐため。
Pester 5 は除外(ExcludeTag)をタグ(Tag)より優先するため、-Tag に明示したタグは既定の除外から取り除く(-Tag Slow・-Tag Gui が 0 件にならない)。-ExcludeTag を渡すと既定の除外が丸ごと置き換わる(-Tag Slow -ExcludeTag Manual は今までどおり動く)。
-Tag・-ExcludeTag はカンマ区切りの文字列でも受け取る。powershell.exe -File で呼ぶと -Tag Unit,Meta は配列にならず 1 つの文字列で渡るため(pre-commit フックがこの呼び方)。
カバレッジ
- 対象は
scripts/の.ps1のうち、画面層のgui.ps1・gui_main.ps1・shell.ps1・app_host.ps1・splash.ps1・*_tab.ps1・*_dialog.ps1、画面ごとのフォルダ(tebunko/ui/search/・tebunko/ui/index/・tebunko/ui/settings/)の中で同じ名前のテスト(tests/tebunko/ui/search/<名前>.Tests.ps1など)が無い画面層のファイルと、startGuiの入口の部品(tebunko/ui/shell/のregions・screens・services・settings_recovery・window_events・closing・startup・activate_watchの 8 ファイル。名前で除く)を除いたもの。ui/の下でも、判断や状態をテストしているファイル(result_list.ps1・preview.ps1・*_view.ps1・ui/shell/nav.ps1など)は対象に残す(tests/helpers/coverage_targets.ps1のgetCoverageTargets。tests/run.ps1が使う)。除いたものは自動テストの対象外で、手で確かめる。画面層のファイルを足したら、この条件から外れているか(分母に入っていないか)を確かめる。 - 値は Pester のコマンド単位(実行されたコマンドの数 ÷ 全コマンドの数。小数点以下 1 桁)。
- 下限は
tests/coverage.baseline(90.0)。-Ciはこれを下回ると失敗し、CI の必須チェックtestが通らない。下回ったらテストを足して戻し、下限は下げない。実測が上がっても下限は上げない(変えるのはメンテナだけ)。 - 全体の目標値は決めない。判断層は 95% 以上を保ち、数字を上げるためだけのテスト(結果を確かめないもの)は書かない(AGENTS.md「テストカバレッジの方針」)。
- カバレッジはブレークポイントを使わない計測(Pester の Profiler。
CodeCoverage.UseBreakpoints = $false)で取る。ブレークポイントを使う計測より速い。 -Ciはカバレッジを Cobertura 形式の XML(work\test\coverage.xml)にも書き出す。Pester が書き出す XML には利用者名を含む絶対パスが入るため使わず、tests/run.ps1が結果(コマンド単位)から行単位に組み立てる。1 行に実行されたコマンドが 1 つでもあれば、その行は通ったものとする。そのため、行単位の値(Codecov の表示)はコマンド単位の値と少し違う。ファイル名はリポジトリからの相対パスで書き、利用者名を含む絶対パスは入れない。