開発・技術選定

Django のテストを書き始める最小セット — 何から書くと元が取れるか

テストは大事だと分かっていても、受託開発では工数が限られます。
「全部書く」は非現実的なので、書けば必ず元が取れる場所から書いています。

1. 権限のテスト(最優先)

事故ったときの被害が最大なので、ここだけは必ず書きます。

class OrderAccessTest(TestCase):
    def setUp(self):
        self.a = User.objects.create_user("a", password="x")
        self.b = User.objects.create_user("b", password="x")
        self.order_a = Order.objects.create(user=self.a, total=1000)

    def test_others_order_is_not_visible(self):
        self.client.force_login(self.b)
        res = self.client.get(f"/orders/{self.order_a.id}/")
        self.assertEqual(res.status_code, 404)

    def test_anonymous_is_redirected(self):
        res = self.client.get(f"/orders/{self.order_a.id}/")
        self.assertEqual(res.status_code, 302)

403 ではなく 404 を返すのがおすすめです。
403 だと「そのIDのデータは存在する」と教えることになります。

2. 金額計算のテスト

計算がズレると信用を失います。境界値を必ず入れます。

def test_tax_rounding(self):
    self.assertEqual(calc_total(100, rate=0.1), 110)
    self.assertEqual(calc_total(101, rate=0.1), 111)   # 端数の丸め
    self.assertEqual(calc_total(0, rate=0.1), 0)

3. 状態遷移のテスト

「キャンセル済みの注文を再度キャンセルできてしまう」系のバグは、
テストが無いと必ず後から出ます。

def test_cannot_cancel_twice(self):
    order = Order.objects.create(status="cancelled")
    with self.assertRaises(InvalidTransition):
        order.cancel()

4. 主要画面が 200 を返すこと(スモークテスト)

網羅性は低いですが、費用対効果が最も高いテストです。
テンプレートの構文ミスや、参照している変数の消失を確実に捕まえます。

class SmokeTest(TestCase):
    def test_public_pages(self):
        for path in ["/", "/about/", "/magazine/"]:
            with self.subTest(path=path):
                self.assertEqual(self.client.get(path).status_code, 200)

subTest を使うと、どのパスで落ちたかが明確に出ます。

書かないと決めているもの

  • 単純なモデルの getter / setter
  • Django 本体の機能(ORM が保存できることのテストなど)
  • 画面の見た目(CSS の当たり方)

ここに時間を使うと、本当に守るべき場所が手薄になります。

実行を習慣にする

python manage.py test --parallel --keepdb

--keepdb でDB作成をスキップすると、実行時間が大幅に短くなります。
速いテストしか習慣にはなりません。

まとめ

優先順位は「権限 → 金額 → 状態遷移 → スモーク」。
この4つだけでも、引き渡し後に発覚する事故はかなり減らせます。