28:テストの実行順序に依存しないテストを書く

「なぜかテストが落ちるようになった」、そんなことはありませんか? 各テストメソッドが他のテストメソッドに依存していると、実行の順序が変わったタイミングでテストが失敗するようになります。

テストの実行順序に依存したテストにはどういった問題があるのでしょうか?

具体的な失敗

リスト 1.15 tests.py
import pytest


class TestSum:
    def setup(self):
        self.data = [0, 1, 2, 3]

    def test_sum(self):
        self.data.append(4)
        actual = sum(self.data)
        assert actual == 10

    def test_negative(self):
        self.data.append(-5)
        actual = sum(self.data)
        assert actual == 5

    def test_type_error(self):
        self.data.append(None)
        with pytest.raises(TypeError):
            sum(self.data)

このテストは、各テストメソッドが他のメソッドに依存しています。 self.data の中身を変更し続けているので、テストが上から順番に実行されないと成功しません。

問題は、1つのテストメソッドとして「正しさ」の保証ができない点です。 1つのテストとして、何をもって「正しさ」を保証しているかが曖昧になります。 テストメソッドが独立していれば、そのテストメソッドだけで正しさを保証できます。 他のテストに依存していると、テストを分離したり、移動したり、足したり、消したりするとテストが壊れてしまいます。

また、テストが依存し合っていると、単純に読みにくくなる場合が多いでしょう。 単一のテストメソッド以外のデータや処理も見る必要があるからです。

ベストプラクティス

まずデータを使い回さないようにしましょう。

import pytest


class TestSum:
    def test_sum(self):
        assert sum([0, 1, 2, 3, 4]) == 10

    def test_negative(self):
        assert sum([0, 1, 2, 3, 4, -5]) == 5

    def test_type_error(self):
        with pytest.raises(TypeError):
            sum([1, None])

見た目の記述量が多く、冗長になっている印象を受けるかもしれません。ですが単体テストは少し冗長なくらいが良いです。 単体テストで確認する内容に関係しないコードはなくすべきですが、メソッド間で共通のデータを持つのはやめましょう。