概要
バックエンドのテストコードの書き方を、行き当たりばったりで学んできました。実装コードに比べて体系的な情報が少なく、「どう書けば実務に耐えるテストになるのか」を独学でつかむまでにずいぶん遠回りをしたと感じています。
本記事では、4年ぐらいテストコードについて試行錯誤しながら何をどう学んできたか振り返ってみます。
この記事のターゲット
主な読者は次のような方を想定しています。
- これからテストコードを書き始めたいバックエンドエンジニア
- なんとなくテストコードを書いてはいるが、「よいテストコード」の判断軸に自信が持てない方
対象はWebアプリケーションに絞っていますが、考え方自体はほかのドメインでも応用できるはずです。
記事の構成
テストコードを書く最初の一歩から、プロジェクト全体でテストコードを運用できるようになるまでにやったこと・悩んだことなどを3つのステップに整理しました。
各ステップでは、参考になった書籍・リポジトリ・ドキュメントを添えています。気になったものから拾い読みしていただける構成です。
また、今回の記事はペアで執筆しているので、ペアの矢野さんにも随所にコメントを添えてもらっています。
矢野さんはテストコードについてどのような経験をしてきたのか、簡単に載せておきます。
矢野プロフィール-テストコードについて
テストコードの経験ライトコードに入ってからなので3年くらいで、現在は物件データのインポートや名寄せ処理のテストコードをよく書いてます。
開発言語はRubyでフレームワークはrspecを使用しています。データの量や種類が多いので、テストコードの書きがいがあります。
前職では、テストコードがないプロジェクトに1~2年ほど在籍していて、それによる予期しないバグで、サービス残業を強いられることがよくあったので、テストコードの恩恵はすごく感じている立場です笑
余談:AI時代にテストコードの書き方を学ぶ意義
AIによるコーディング支援が当たり前になり、TDDとAIを組み合わせた開発スタイルもよく耳にするようになりました。テストコードもAIに書いてもらえる時代です。
しかし、生成されたテストコードがプロジェクトに本当に役立つのか、判断するには人間側の知識や経験が必要です。
つまり、何がいいテストコードなのか見極められるようになることは、今の時代においても重要だと考えています。
そして何より、テストコードを書くこと自体が単純に楽しいので、趣味として手を動かして理解を深めていくのもよいものです。
「生成されたテストコードがプロジェクトに本当に役立つのか、判断するには人間側の知識や経験が必要です。」
矢野コメント:
最近、AIエージェント等でテストコードを生成してもらったとき、「テストは通るけど、これって本当に意味のある検証をしてるのか?」と感じることが何度かありました。
テストが増えると一見安心感があるのですが、カバレッジを埋めるためだけのテストは、むしろ変更のコストを上げるだけになりかねないんですよね。
AIが書いたテストに「GOを出せる目」を養うことが、これからのエンジニアに求められるスキルだと改めて思います。
ステップ1:はじめてのテスティングフレームワーク
最初の一歩は、テスティングフレームワークの導入から始まりました。言語やツールは自分の好きなものを選ぶのがよさそうですが、これから始めるなら pytest がやりやすいと思います。
最近は uv で環境構築も楽になったので、サクッとテストコードを書くための準備が整います。
このステップで学びたいこと
まずはテスティングフレームワークの使い方を押さえれば、最初のハードルは越えられます。あわせて、テストコードを書くときの基本構文として Given-When-Then を常に意識しておくと、読み書きするときに迷子になりにくくなります。
参考リポジトリ
テスティングフレームワークは自分自身をテストするテストコードを書いている場合が多く、参考にするのにおすすめです。たとえばpytestのリポジトリのテストコードを覗くと、テストコードの基本的な書き方とpytestの挙動の両方を理解できます(参考)。
ボリュームは多いので、気になったところを流し読みするだけでも十分力はつきます。ここでは特に面白いと感じた部分を抜粋します。
出典: GitHub - pytest-dev/pytest: https://github.com/pytest-dev/pytest
# https://github.com/pytest-dev/pytest/blob/main/testing/logging/test_fixture.py
# testing/logging/test_fixture.py
logger = logging.getLogger(__name__)
sublogger = logging.getLogger(__name__ + ".baz")
# fixture の使い方の雰囲気をつかむのに Good
@pytest.fixture(autouse=True)
def cleanup_disabled_logging() -> Iterator[None]:
"""Simple fixture that ensures that a test doesn't disable logging.
This is necessary because ``logging.disable()`` is global, so a test disabling logging
and not cleaning up after will break every test that runs after it.
This behavior was moved to a fixture so that logging will be un-disabled even if the test fails an assertion.
"""
yield
logging.disable(logging.NOTSET)
# 特定のログレベルのログのみが記録されたかを検証するテストコード
# caplog の使い方を知れるだけでなく、Given-When-Then の構造をつかむのにもよいサンプル
# 一例としてコメントでGiven-When-Thenを添える
def test_change_level(caplog: pytest.LogCaptureFixture) -> None:
# Given
caplog.set_level(logging.INFO)
# When
logger.debug("handler DEBUG level")
logger.info("handler INFO level")
caplog.set_level(logging.CRITICAL, logger=sublogger.name)
sublogger.warning("logger WARNING level")
sublogger.critical("logger CRITICAL level")
# THEN
assert "DEBUG" not in caplog.text
assert "INFO" in caplog.text
assert "WARNING" not in caplog.text
assert "CRITICAL" in caplog.text膨大なテストコードの中から参考になりそうな箇所を探すときは、AIの力を借りるとぐっと捗ります。ログ・一時ディレクトリ・モックなど、自分が使いたい機能を起点に探してもらうイメージです。
たとえば以下のようなプロンプトを渡すことで、testing/test_tmpdir.py の存在を知ることができます。
…
記事の続きは下のURLをクリック!
https://rightcode.co.jp/blogs/55660
もっとワクワクしたいあなたへ
現在、ライトコードでは「WEBエンジニア」「モバイルエンジニア」「データエンジニア」「ゲームエンジニア」「デザイナー」「WEBディレクター」「営業」などを積極採用中です!
ライトコードは技術力に定評のある受託開発をメインにしているIT企業です。
有名WEBサービスやアプリの受託開発などの企画、開発案件が目白押しの状況です。
- もっと大きなことに挑戦したい!
- エンジニアとしてもっと成長したい!
- モダンな技術に触れたい!
現状に満足していない方は、まずは、エンジニアとしても第一線を走り続ける弊社代表と気軽にお話してみませんか?
ネット上では、ちょっとユルそうな会社に感じると思いますが(笑)、
実は技術力に定評があり、沢山の実績を残している会社ということをお伝えしたいと思っております。
- ライトコードの魅力を知っていただきたい!
- 社風や文化なども知っていただきたい!
- 技術に対して熱意のある方に入社していただきたい!
一度、【Wantedly内の弊社ページ】をのぞいてみてください。