note-mcpのテスト戦略と実行方法について説明します。
| 種類 | 場所 | 説明 |
|---|---|---|
| ユニットテスト | tests/unit/ |
個別モジュールの単体テスト |
| 統合テスト | tests/integration/ |
複数モジュール間の結合テスト |
| E2Eテスト | tests/e2e/ |
実環境でのエンドツーエンドテスト |
# すべてのテストを実行
uv run pytest
# 詳細出力で実行
uv run pytest -v
# 特定のテストを実行
uv run pytest tests/unit/test_markdown.py::test_heading -v# E2Eテストのみ実行
uv run pytest -m e2e
# 認証が必要なテストをスキップ
uv run pytest -m "not requires_auth"
# Dockerが必要なテストをスキップ
uv run pytest -m "not docker"E2Eテストは実際のnote.com環境で記事操作とMarkdown変換を検証します。
プロジェクトルートに.envファイルを作成し、認証情報を設定します:
# .env
NOTE_USERNAME=your_username
NOTE_PASSWORD=your_password重要:
.envファイルはgitにコミットしないでください。.gitignoreで除外されています。
dotenvxを使用すると、環境変数を暗号化して安全に管理できます:
# dotenvxのインストール
npm install -g @dotenvx/dotenvx
# .envを暗号化
dotenvx encrypt
# テスト実行時に復号化
dotenvx run -- uv run pytest tests/e2e/ -vE2Eテストのreal_sessionフィクスチャは以下の優先順位でセッションを取得します:
- 保存済みセッション: 有効期限内のセッションがあれば使用
- 自動ログイン: 環境変数(
NOTE_USERNAME,NOTE_PASSWORD)があれば自動ログイン - 手動ログイン: どちらもない場合は手動ログイン待機(300秒タイムアウト)
CI/CD環境では環境変数を設定することで、完全自動でE2Eテストを実行できます。
自動ログイン中にreCAPTCHAや2FAが検出されると、LoginError例外が発生し、テストはスキップされます:
SKIPPED: E2Eテスト用セッション取得失敗: reCAPTCHAが検出されました
この場合は、手動でログインしてセッションを保存してください。詳細は認証ガイドを参照してください。
# すべてのE2Eテストを実行
uv run pytest tests/e2e/ -v
# 特定のテストを実行
uv run pytest tests/e2e/test_markdown_conversion.py -v
# 失敗時に詳細情報を表示
uv run pytest tests/e2e/ -v --tb=shortE2Eテストは以下のパターンで記事を管理します:
- 作成: テスト開始時に
[E2E-TEST-{timestamp}]プレフィックス付きで下書き作成 - 検証: プレビューページのHTML要素でMarkdown変換を検証
- 削除: テスト終了後に自動クリーンアップ(ベストエフォート)
Markdown変換テストは以下の要素を検証します:
| 要素 | 入力例 | 検証内容 |
|---|---|---|
| 見出しH2 | ## 見出し |
<h2>要素として変換される |
| 見出しH3 | ### 見出し |
<h3>要素として変換される |
| 打消し線 | ~~text~~ |
<s>要素として変換される |
| 太字 | **text** |
<strong>要素として変換される |
| コードブロック | ```code``` |
<pre><code>要素として変換される |
| 中央配置 | ->text<- |
text-align: centerスタイルが適用される |
| 右配置 | ->text |
text-align: rightスタイルが適用される |
| 目次 | [TOC] |
目次HTMLとして変換される |
| 引用 | > text |
<blockquote>要素として変換される |
| 箇条書き | - text |
<ul><li>要素として変換される |
| 番号付き | 1. text |
<ol><li>要素として変換される |
通常のMarkdown変換テストに加えて、ネイティブHTML検証テストを提供しています。
従来のE2Eテストには「トートロジー」問題がありました:
update_article()が内部でmarkdown_to_html()を使用してHTMLを生成- 生成されたHTMLがそのままプレビューページに表示される
- つまり「自己生成HTMLを自己検証している」状態
これではnote.comプラットフォームが実際に生成するHTMLを検証できません。
ネイティブHTML検証テストは、この問題を解決します:
- エディタに直接入力: キーボード操作でMarkdown記法をエディタに入力
- ProseMirror変換: スペースをトリガーにProseMirrorがMarkdownを変換
- 保存とプレビュー: 変換結果を保存し、プレビューページを開く
- ネイティブHTML検証: note.comが生成したHTMLを検証
これにより、実際のユーザー体験を反映したテストが可能になります。
# 見出し変換テスト
uv run pytest tests/e2e/test_native_html_validation.py::TestNativeHeadingConversion -v
# 打消し線変換テスト
uv run pytest tests/e2e/test_native_html_validation.py::TestNativeStrikethroughConversion -v
# すべてのネイティブ検証テスト
uv run pytest tests/e2e/test_native_html_validation.py -v| テストケース | 優先度 | 入力 | 期待結果 |
|---|---|---|---|
| H2見出し | P1 | ## text + スペース |
<h2>text</h2> |
| H3見出し | P1 | ### text + スペース |
<h3>text</h3> |
| 打消し線 | P1 | ~~text~~ + スペース |
<s>text</s> |
| 太字 | P1 | **text** + スペース |
<strong>text</strong> |
| コードブロック | P2 | ``` |
<pre><code> |
| 中央揃え | P2 | ->text<- |
text-align: center |
| 右揃え | P2 | ->text |
text-align: right |
| 目次 | P2 | [TOC] |
目次HTML |
| 引用 | P2 | > text |
<blockquote> |
| 箇条書き | P2 | - text |
<ul><li> |
| 番号付き | P2 | 1. text |
<ol><li> |
E2Eテストは外部サービス(note.com)に依存するため、一時的なネットワークエラーでテストが失敗することがあります。with_retryヘルパー関数を使用することで、一時的なエラーから自動的に回復できます。
from tests.e2e.helpers import with_retry
# API呼び出しをリトライ対象にする
article = await with_retry(lambda: create_draft(session, input))
# タイムアウトエラーが発生しても最大3回まで自動リトライ
result = await with_retry(lambda: update_article(session, article_id, data))| パラメータ | 型 | デフォルト | 説明 |
|---|---|---|---|
func |
Callable[[], Awaitable[T]] |
(必須) | リトライ対象の非同期関数 |
max_attempts |
int |
3 |
最大試行回数 |
backoff_base |
float |
1.0 |
バックオフ基準時間(秒) |
retryable_exceptions |
tuple[type[Exception], ...] |
RETRYABLE_EXCEPTIONS |
リトライ対象の例外 |
リトライ間隔は指数バックオフで増加します(max_attempts=3の場合、最大2回のリトライ):
- 1回目の試行: 失敗 → 1秒待機してリトライ
- 2回目の試行: 失敗 → 2秒待機してリトライ
- 3回目の試行: 失敗 → 例外を発生
これにより、一時的な障害からの回復時間を確保しつつ、永続的な障害に対しては迅速に失敗します。
以下の例外は一時的なネットワークエラーとしてリトライされます:
| 例外 | 説明 |
|---|---|
TimeoutError |
一時的なタイムアウト |
asyncio.TimeoutError |
非同期タイムアウト |
httpx.TimeoutException |
HTTPタイムアウト |
httpx.NetworkError |
ネットワーク接続エラー |
PlaywrightError(タイムアウト系) |
ブラウザ操作のタイムアウト |
使うべき場面:
- API呼び出し(
create_draft,update_articleなど) - ブラウザでの画像アップロード
- プレビューページの取得
使わなくてよい場面:
- ローカルファイル操作
- Markdown変換(純粋な変換処理)
- テストのセットアップ/クリーンアップ
リトライが発生すると、ログに記録されます:
WARNING - Attempt 1 failed: TimeoutError. Retrying in 1.0s...
WARNING - Attempt 2 failed: TimeoutError. Retrying in 2.0s...
これにより、CI/CDログでフレイキーテストの原因を追跡できます。
MCPツールテストは、note-mcpが提供する17個のMCPツールの動作を検証するE2Eテストです。
| カテゴリ | ツール | 説明 |
|---|---|---|
| 認証フロー | note_login |
ブラウザでnote.comにログイン |
note_check_auth |
認証状態を確認 | |
note_logout |
セッションをクリア | |
note_set_username |
ユーザー名を設定 | |
| 記事CRUD | note_create_draft |
下書き記事を作成 |
note_get_article |
記事の内容を取得 | |
note_update_article |
記事を更新 | |
note_publish_article |
記事を公開 | |
note_list_articles |
記事一覧を取得 | |
note_create_from_file |
ファイルから記事を作成 | |
note_delete_draft |
下書きを削除 | |
note_delete_all_drafts |
全下書きを削除 | |
| 画像操作 | note_upload_eyecatch |
アイキャッチ画像をアップロード |
note_upload_body_image |
本文用画像をアップロード | |
note_insert_body_image |
画像を記事に挿入 | |
| プレビュー | note_show_preview |
記事のプレビューを表示 |
note_get_preview_html |
プレビューHTMLを取得 |
# すべてのMCPツールテストを実行
uv run pytest tests/e2e/test_mcp_tools.py -v
# 認証フローテストのみ
uv run pytest tests/e2e/test_mcp_tools.py::TestAuthenticationFlow -v
# 記事CRUDテストのみ
uv run pytest tests/e2e/test_mcp_tools.py::TestArticleCRUD -v
# 画像操作テストのみ
uv run pytest tests/e2e/test_mcp_tools.py::TestImageOperations -v
# プレビューテストのみ
uv run pytest tests/e2e/test_mcp_tools.py::TestPreviewOperations -vMCPツールテストは以下の順序で実行する必要があります:
-
認証フロー (
TestAuthenticationFlow)- 他のすべてのテストの前提条件
note_login→note_check_auth→note_logoutの順で検証
-
記事CRUD (
TestArticleCRUD)- 認証済み状態が必要
note_create_draft→note_get_article→note_update_article→note_list_articles
-
画像操作 (
TestImageOperations)- 認証済み状態と下書き記事が必要
- テスト用画像ファイルが必要
-
プレビュー (
TestPreviewOperations)- 認証済み状態と下書き記事が必要
テストで使用するサンプルデータ:
# tests/e2e/conftest.py で定義
test_image_path = Path(__file__).parent / "assets" / "test_image.png" # 100x100 PNG画像@mcp.tool()デコレータは関数をFunctionToolオブジェクトにラップします。テストでMCPツール関数を直接呼び出す場合は、.fn属性経由で元の関数にアクセスする必要があります:
# ❌ 動作しない(FunctionToolは直接呼び出せない)
result = await note_check_auth()
# ✅ 正しい呼び出し方法
result = await note_check_auth.fn()詳細はtests/e2e/test_mcp_tools.pyのdocstringを参照してください。
note_create_from_file MCPツールの動作を検証するE2Eテストです。Markdownファイルから記事を作成し、プレビューページで実際の表示を確認します。
| ファイル | 説明 |
|---|---|
tests/e2e/test_create_from_file.py |
ファイルベース記事作成テスト |
tests/e2e/helpers/validation.py |
プレビュー検証ヘルパー |
tests/e2e/helpers/image_utils.py |
画像検証ヘルパー |
| テストケース | 検証内容 |
|---|---|
test_create_from_frontmatter_file |
YAMLフロントマター付きファイルから記事作成 |
test_create_from_h1_only_file |
H1のみのファイルからタイトル抽出して記事作成 |
test_create_from_toc_file |
[TOC]マーカー付きファイルから目次生成 |
test_create_from_math_file |
数式付きファイルからKaTeXレンダリング検証 |
test_create_with_local_image_upload |
ローカル画像をアップロードしてプレビュー検証 |
test_create_from_toc_and_image_file |
TOC + 画像の組み合わせ検証 |
PreviewValidatorクラスを使用して、プレビューページのHTML要素を検証します:
from tests.e2e.helpers import PreviewValidator, open_preview_for_article_key
# プレビューページを開く
preview_page = await open_preview_for_article_key(page, article_key)
validator = PreviewValidator(preview_page)
# TOC検証
toc_result = await validator.validate_toc()
assert toc_result.success, f"TOC validation failed: {toc_result.message}"
# 数式検証(KaTeXレンダリング)
math_result = await validator.validate_math()
assert math_result.success, f"Math validation failed: {math_result.message}"
# 特定の数式テキストを検証
inline_result = await validator.validate_math("E = mc")
assert inline_result.success, f"Inline math validation failed: {inline_result.message}"note.comはKaTeXを使用して数式をレンダリングします。validate_math()メソッドは.katexクラス要素の存在を確認します:
| パラメータ | 型 | デフォルト | 説明 |
|---|---|---|---|
formula_text |
str | None |
None |
期待される数式テキスト(部分一致) |
timeout_ms |
int |
5000 |
要素出現待機のタイムアウト |
# 数式要素の存在のみ確認
result = await validator.validate_math()
# 特定の数式テキストを含む要素を確認
result = await validator.validate_math("\\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a}")ImageValidatorクラスを使用して、プレビューページの画像表示を検証します:
from tests.e2e.helpers import ImageValidator
image_validator = ImageValidator(preview_page)
# 画像の存在確認(期待する件数を指定)
result = await image_validator.validate_image_exists(expected_count=1)
assert result.success, f"Image validation failed: {result.message}"# すべてのファイルベース記事作成テストを実行
uv run pytest tests/e2e/test_create_from_file.py -v
# 数式テストのみ
uv run pytest tests/e2e/test_create_from_file.py::TestCreateFromFile::test_create_from_math_file -v
# TOC + 画像の組み合わせテスト
uv run pytest tests/e2e/test_create_from_file.py::TestCreateFromFile::test_create_from_toc_and_image_file -v症状:
セッションが無効です
解決方法:
- 環境変数が正しく設定されているか確認
- dotenvxを使用している場合は
dotenvx run --を付けて実行
症状:
- テスト後に
[E2E-TEST-...]プレフィックスの下書きが残っている
解決方法:
- テストが中断された場合、手動でダッシュボードから削除
- 正常終了時は自動削除されるが、ネットワークエラー等でスキップされる場合がある
症状:
Expected element not found: h2#heading-text
解決方法:
- note.comのエディタ仕様変更の可能性
- プレビューURLをログで確認し、ブラウザで目視確認
- セレクタが最新のDOM構造と一致しているか確認
docker compose run --rm testdocker compose run --rm test-headed# VNC環境起動
docker compose up -d test-vnc
# VNCクライアントでアクセス
vncviewer localhost:5900Note:
test-vncサービスはVNCポート(5900)のみ公開しています。 noVNC (http://localhost:6080) を使用する場合はdevサービスを利用してください。
詳細はREADME.mdを参照してください。
# カバレッジレポート生成
uv run pytest --cov=src/note_mcp --cov-report=html
# レポートを表示
open htmlcov/index.htmlGitHub Actionsでは以下の構成でテストが実行されます:
- ユニットテスト・統合テスト: 毎コミット
- E2Eテスト: 手動トリガーまたはリリース前
Note: E2EテストはCI環境で実行するには認証情報のSecrets設定が必要です。