LoginSignup
8
6

More than 5 years have passed since last update.

pythonでgoのtable driven testを書くならsubTestを使うと良いのかも

Last updated at Posted at 2017-07-06

はじめに

go界隈でtable driven testと呼ばれる形式でunit testを書く事が好まれているらしい(とは言え、他の言語でも同様のものは存在したりはしていたので発明したというわけではないけれど)。

pythonでtable driven testを書くならどうすると良いかと言うことを考えてみた。

unittestで普通に書くと長い

例えば、以下のようなaddという関数をテストするとする。

def add(x, y):
    return x + y

pythonのunittestモジュールの範囲で愚直に書くと以下の様な感じ。

import unittest


class Tests(unittest.TestCase):
    def _callFUT(self, x, y):
        from add import add
        return add(x, y)

    def test_with_positive(self):
        actual = self._callFUT(10, 10)
        self.assertEqual(actual, 20)

    def test_with_zero(self):
        actual = self._callFUT(10, 0)
        self.assertEqual(actual, 10)

    def test_with_negative(self):
        actual = self._callFUT(10, -10)
        self.assertEqual(actual, 0)

    def test_with_biiiiiiiiiig(self):
        actual = self._callFUT(
            10,
            10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000,
        )
        self.assertEqual(
            actual,
            10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010,
        )

長い。だるい。

(callFUTについては信仰的なもの。詳しくはこの辺を参照)

table driven test

table driven testにするとシュッとした感じになる。subTestを使うと良さそう。

import unittest
from collections import namedtuple


class Tests(unittest.TestCase):
    def _callFUT(self, x, y):
        from add import add
        return add(x, y)

    def test_it(self):
        C = namedtuple("C", "msg x y expected")
        candidates = [
            C(msg="with positive", x=10, y=10, expected=20),
            C(msg="with zero", x=10, y=0, expected=10),
            C(msg="with negative", x=10, y=-10, expected=0),
            C(
                msg="with biiiiiiig",
                x=10,
                y=10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000,
                expected=10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010,
            ),
        ]
        for c in candidates:
            with self.subTest(msg=c.msg):
                actual = self._callFUT(c.x, c.y)
                self.assertEqual(actual, c.expected)

以下は個人的な好み

  • namedtupleを利用している
  • msgも一緒に書く
  • _callFUT(testメソッドが一つで終わるのなら分けなくても良いかもしれない)

subTestの件数の集計方法

成功したときには1件としてカウントされる。

.
----------------------------------------------------------------------
Ran 1 test in 0.002s

OK

失敗したときにはN件としてカウントされるので良い。


======================================================================
FAIL: test_it (__main__.Tests) [with positive]
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests.py", line 26, in test_it
    self.assertEqual(actual, c.expected + 1)
AssertionError: 20 != 21

======================================================================
FAIL: test_it (__main__.Tests) [with zero]
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests.py", line 26, in test_it
    self.assertEqual(actual, c.expected + 1)
AssertionError: 10 != 11

======================================================================
FAIL: test_it (__main__.Tests) [with negative]
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests.py", line 26, in test_it
    self.assertEqual(actual, c.expected + 1)
AssertionError: 0 != 1

======================================================================
FAIL: test_it (__main__.Tests) [with biiiiiiig]
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests.py", line 26, in test_it
    self.assertEqual(actual, c.expected + 1)
AssertionError: 10000[33 chars]000000000000000000000000000000000000000000000000000000000000010 != 10000[33 chars]000000000000000000000000000000000000000000000000000000000000011

----------------------------------------------------------------------
Ran 1 test in 0.003s

FAILED (failures=4)

pytestを使った場合

pytestを使ったtable driven testについては誰か書いてくれたりしないかな。

8
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6