単体テストとは?基本的な書き方と実装手順を初心者向けに解説

Published on: | Last updated:

単体テスト。なんだろう、これ。よく聞くけど。

プログラムが、ちゃんと動くか確かめること…らしい。 小さい単位で。 関数とか、メソッドとか。 うん、わかるような、わからないような。

とりあえず、結論から言うと

単体テストは、「未来の自分を助けるための保険」みたいなもの、かな。コードを触るのが怖くなくなる、お守り。

これを書いておけば、後で何か変更したときに、意図せず何かを壊してないかすぐわかる。その安心感が、たぶん一番大事。

なんでテストが必要に感じたか

昔、ちょっとした修正のつもりが、全然関係ない場所の機能を壊したことがあって。それに気づいたのが、ずっと後になってからで…。もう、最悪だった。

あの時、もしテストがあれば。「この変更で、ここが動かなくなりましたよ」って、すぐに教えてくれたはず。後工程での手戻りを減らせる、っていうのはこういうことか。

バグが見つかるのが早ければ早いほど、修正は楽。 これは本当にそう思う。だから、品質のため、というより、自分の精神衛生のために必要、って感じてる。

テストがあることの安心感のイメージ
テストがあることの安心感のイメージ

じゃあ、どう書くの? 基本の考え方

なんか、3つのステップがあるらしい。「準備・実行・検証」。英語だと Arrange, Act, Assert って言うみたい。

  • 準備 (Arrange): テストしたい関数を動かすための、お膳立て。データとか、オブジェクトとかを用意する。
  • 実行 (Act): 実際に、その関数を呼び出す。
  • 検証 (Assert): 実行した結果が、期待通りだったかチェックする。ここで「こうなるはずだ」っていうのを書く。

この「検証」がキモだよね。結果が期待通りならテストは成功。違ったら失敗。すごくシンプル。

あと、テスト駆動開発(TDD)っていう考え方もあるらしい。 これは、先に失敗するテストを書いて、そのテストをパスするために最小限のコードを書いて、きれいにしていく…っていうサイクルを回す開発手法。 Red/Green/Refactorって呼ばれてる。 うーん、これはちょっと上級者向けかな。まずは動くコードにテストを書くところから始めたい。

準備・実行・検証の3ステップの流れ
準備・実行・検証の3ステップの流れ

試しに書いてみる(Pythonの場合)

日本の記事だとJavaのJUnitが多い気がするけど、ここではPythonの`pytest`を使ってみる。こっちのほうがシンプルに書けるらしいから。


# product.py
# これがテスト対象のコード
def add(a, b):
  """二つの数を足し算する関数"""
  return a + b

# test_product.py
# こっちがテストコード
import pytest
from product import add

def test_add_positive_numbers():
  # 準備 (Arrange) は特にないけど、心の中では。
  # 実行 (Act) と 検証 (Assert) を一緒に
  assert add(2, 3) == 5

def test_add_negative_numbers():
  # 期待値がマイナスになるケースも試す
  assert add(-1, -1) == -2

def test_add_with_zero():
  # ゼロを足すケースも大事
  assert add(5, 0) == 5
  

これだけ。すごく短い。`test_`で始まる関数を書いて、その中で`assert`を使って「`add(2, 3)`の結果は`5`であるべき」と書くだけ。 JUnitみたいに、`assertEqual`とか専用のメソッドを覚える必要がないのが、個人的には楽。

フレームワークによる考え方の違い (JUnit vs pytest)

ちょっと調べてみたら、Javaでよく使われるJUnitとPythonのpytestでは、書き方だけじゃなくて、思想も少し違うみたいで面白い。 日本だとやっぱりJavaの情報が多いから、こういう違いを知っておくといいかも。

JUnitとpytestの個人的な感想
項目 JUnit (Java) pytest (Python)
書き方の印象 クラスベース。ちょっと「かっちり」してる感じ。 シンプルな関数で書ける。始めやすい。 ボイラープレートコードが少ないっていうのは、こういうことか。
アサーション(検証) `assertEquals(5, result)` みたいな専用メソッドを使う。 `assert result == 5` みたいに、普通のPythonの`assert`文。直感的。
テストの発見 特定の命名規則(`@Test`アノテーションとか)が必要。明示的。 `test_`で始まるファイルや関数を自動で見つけてくれる。楽ちん。
セットアップ/後処理 `@BeforeEach`, `@AfterEach` アノテーション。わかりやすいルール。 「フィクスチャ」っていう仕組みが強力らしい。まだよくわかってないけど、もっと柔軟なことができるっぽい。

どっちが良い悪いじゃないけど、pytestの手軽さは初心者にはありがたいかも。

テストがあるコードとないコードの構造イメージ
テストがあるコードとないコードの構造イメージ

でも、大変なこともある

もちろん、いいことばかりじゃない。デメリットもちゃんとある。

  • 手間がかかる: 当然だけど、テストコードを書く時間は増える。 短期的には開発スピードが落ちると感じるかも。
  • スキルが必要: どんなテストを書けばいいか考えるのは、結構スキルがいる。 境界値とか異常系とか、考えることは多い。
  • メンテナンス: プロダクトコードを変えたら、テストコードも直さないといけない。これが地味に面倒くさい。

だから、なんでもかんでも100%テストを書く、っていうのは現実的じゃないかもしれない。 特に、UIとか、外部のDBに接続する部分とかは、テストが書きにくい。 まずは、ビジネスロジックの核となる部分から始めるのが良さそう。

まとめ、というか今の気持ち

単体テストは、完璧を目指すためのものじゃなくて、最低限の品質と、未来の開発者の安心を担保するためのもの、っていう感じかな。

最初は面倒だけど、一度あの「バグを早期発見できた」っていう快感を味わうと、やめられなくなるかもしれない。 これから書くコードには、少しずつでもテストを書いていこうと思う。

あなたのプロジェクトで、一番変更するのが怖い関数はどれですか?たぶん、そこが最初の単体テストの書きどころです。

Related to this topic:

Comments