
pytestのプラグインpytest-sugarでテスト結果を見やすくしてみる
はじめに
データ事業本部のkobayashiです。
Pythonのテストフレームワークであるpytestを業務で日常的に使っていると、テストケースが多くなってきた場合にどのテストが失敗したのか、現在どこまで進んでいるのかが分かりにくくなります。
今回は、pytestの出力を視覚的に改善し、開発体験を向上させるpytest-sugar
というプラグインを試してみました。
pytest-sugarとは
pytest-sugarは、pytestの実行結果を見やすく、わかりやすくするためのプラグインです。標準のpytestの出力は機能的ではありますが、視覚的にはシンプルすぎる面があります。pytest-sugarを導入することで、以下のような改善が得られます。
主な特徴:
- プログレスバーによるテスト進捗の可視化
- 失敗やエラーの即座表示
- カラフルで見やすい出力フォーマット
- 各テストの実行時間表示
- Playwright連携機能(トレースファイルの管理)
pytest-sugarを使ってみる
環境
今回使用した環境は以下の通りです。
Python 3.12.8
pytest 8.3.2
pytest-sugar 1.1.1
インストール
pipで簡単にインストールできます。
$ pip install pytest-sugar
基本的な使い方
まず、通常のpytestコードで動作を確認してみます。
import pytest
import time
def calculate_price(base_price: float, tax_rate: float = 0.1) -> float:
"""商品価格に税金を加算した合計金額を計算する"""
if base_price < 0:
raise ValueError("価格は0以上である必要があります")
if not 0 <= tax_rate <= 1:
raise ValueError("税率は0から1の間である必要があります")
return base_price * (1 + tax_rate)
class TestBasicExample:
"""基本的なテストケースの例"""
def test_calculate_price_normal(self):
"""正常な価格計算のテスト"""
result = calculate_price(1000, 0.1)
assert result == 1100
def test_calculate_price_zero(self):
"""価格0円の場合のテスト"""
result = calculate_price(0, 0.1)
assert result == 0
@pytest.mark.parametrize("base_price, tax_rate, expected", [
(1000, 0.1, 1100),
(500, 0.08, 540),
(2000, 0.05, 2100),
])
def test_various_prices(self, base_price, tax_rate, expected):
"""様々な価格と税率の組み合わせをテスト"""
result = calculate_price(base_price, tax_rate)
assert result == expected
通常のpytest実行結果
$ pytest test_basic.py
============================= test session starts ==============================
platform darwin -- Python 3.11.5, pytest-8.3.4, pluggy-1.5.0
rootdir: /path/to/project
collected 5 items
test_basic.py ..... [100%]
============================== 5 passed in 0.01s ===============================
pytest-sugar導入後の実行結果
$ pytest test_basic.py
Test session starts (platform: darwin, Python 3.12.8, pytest 8.3.4, pytest-sugar 1.1.1)
...
test_basic.py ✓✓✓✓✓ 100% ██████████
Results (0.01s):
5 passed
pytest-sugarを導入すると、テストの進行状況がプログレスバーで表示され、各テストの成功が✓マークで表示されます。視覚的に分かりやすくなっているのが確認できます。
失敗時の表示
テストが失敗した場合の表示も改善されます。
def test_intentional_failure():
"""意図的に失敗するテスト"""
assert 1 + 1 == 3
pytest test_basic.py
Test session starts (platform: darwin, Python 3.12.8, pytest 8.4.2, pytest-sugar 1.1.1)
...
test_basic.py ✓✓✓✓✓✓✓✓✓✓✓✓✓ 87% ████████▋
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― TestFailingCases.test_intentional_failure ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
self = <test_basic.TestFailingCases object at 0x108228690>
def test_intentional_failure(self):
"""意図的に失敗するテスト"""
> assert 1 + 1 == 3
E assert (1 + 1) == 3
test_basic.py:80: AssertionError
test_basic.py ⨯✓ 100% ██████████
===================================================================================================== short test summary info =====================================================================================================
FAILED test_basic.py::TestFailingCases::test_intentional_failure - assert (1 + 1) == 3
Results (0.70s):
14 passed
1 failed
- test_basic.py:78 TestFailingCases.test_intentional_failure
このテストを実行すると、pytest-sugarは失敗を即座に赤色で表示し、失敗の詳細を見やすく整形して表示します。
Playwrightとの連携機能
pytest-sugarの面白い機能の一つが、Playwrightとの連携機能です。E2Eテストでは、テスト失敗時のデバッグが重要ですが、pytest-sugarはPlaywrightのトレースファイル管理を支援します。
Playwrightテストの作成
import pytest
from playwright.sync_api import Page, expect
class TestPlaywrightExample:
"""Playwrightを使用したE2Eテストの例"""
def test_python_org_search(self, page: Page):
"""Python.org検索のテスト"""
# Python.orgにアクセス
page.goto("https://www.python.org")
# 検索ボックスに入力
search_box = page.locator('input[name="q"]')
search_box.fill("pytest")
# 検索実行
search_box.press("Enter")
# 検索結果ページが表示されることを確認
page.wait_for_url("**/search/**")
# 検索結果が表示されることを確認
results = page.locator('ul.list-recent-events')
expect(results).to_be_visible()
@pytest.mark.xfail(reason="意図的に失敗させてスクリーンショット機能をテスト")
def test_screenshot_on_failure(self, page: Page):
"""失敗時のスクリーンショット取得テスト"""
page.goto("https://example.com")
try:
# 存在しない要素を探す(失敗する)
page.get_by_text("This text does not exist").click(timeout=3000)
except Exception as e:
# スクリーンショットを保存
page.screenshot(path="test_failure.png")
raise e
@pytest.mark.parametrize("search_term", ["pytest", "django", "flask"])
def test_multiple_searches(self, page: Page, search_term):
"""複数の検索キーワードでテスト(Python.org使用)"""
page.goto("https://www.python.org")
# 検索実行
search_box = page.locator('input[name="q"]')
search_box.fill(search_term)
search_box.press("Enter")
# 検索結果ページが表示されることを確認
page.wait_for_url("**/search/**", timeout=10000)
# 検索結果が表示されることを確認(タイトルは変動するため結果要素で判定)
assert page.url.__contains__("search") or page.url.__contains__("?q=")
# ページが正しくロードされたことを確認
assert page.locator('body').is_visible()
Playwright連携オプション
pytest-sugarは、Playwrightのトレースファイル管理用の特別なオプションを提供しています。
# トレースファイルの保存先を指定
$ pytest --sugar-trace-dir ./traces test_playwright.py
# トレースファイルの検出と表示を無効化
$ pytest --sugar-no-trace test_playwright.py
--sugar-trace-dir
オプションを使用すると、Playwrightのトレースファイルを指定したディレクトリに整理して保存できます。これにより、E2Eテストのデバッグが容易になります。
トレースファイルには以下の情報が含まれます:
- テスト実行時のスクリーンショット
- ネットワークアクティビティ
- コンソールログ
- DOM スナップショット
その他の便利なオプション
非対話環境での使用
CI/CD環境など、非対話的な環境でpytest-sugarを使用する場合は、--force-sugar
オプションを使用することでpytest-sugarの出力を強制することができます。GHAで定期的にテストを行いその情報を管理する場合には非常に有効だと思います。
$ pytest --force-sugar
まとめ
pytest-sugarは、pytestの出力を大幅に改善し、開発体験を向上させる優れたプラグインです。特にPlaywrightとの連携機能は、E2Eテストのデバッグ効率を大きく向上させます。
導入も簡単で既存のテストコードに変更を加える必要がないため、すぐに試すことができます。テスト結果の視認性を改善したい、E2Eテストのデバッグを効率化したいという方には、ぜひ試していただきたいプラグインです。
日々のテスト実行がより快適になり、チーム全体の生産性向上にも貢献できるはずです。