読者です 読者をやめる 読者になる 読者になる

simudaru's blog

Python, Rなどのメモを残していこうと思います。  よろしくお願いいたします。

【python】コードの品質管理について その3 unittest

下記の続き。simudaru.hatenablog.com
simudaru.hatenablog.com

下記を参考にした。
Python Project Howto (日本語訳) — Python Project Howto 日本語訳
d.hatena.ne.jp

unittest

前回に引き続き、fizzbuzzの例を示す。

fizzbuzz.py
# coding: utf-8
""" FizzBuzz問題 """


def fizzbuzz(x):
    """ FizzBuzz関数

    引数xが3で割り切れる場合には'Fizz'を、
    引数xが5で割り切れる場合には'Buzz'を、
    引数xが3でも5でも割り切れる場合には'FizzBuzz'を、
    それ以外の場合は引数xを返す
    """
    if x % 15 == 0:
        ans = 'FizzBuzz'
    elif x % 3 == 0:
        ans = 'Fizz'
    else:
        ans = 'Buzz'
    return ans


参考URLに従い、以下のような構成とした。
./FizzBuzz
|--/src
| |--__init__.py
| |--fizzbuzz.py
|--/test
| |--sample_fizzbuzz.py
|--setup.py

__init__.py
from fizzbuzz import *
test_fizzbuzz.py

unittest.TestCaseを継承したテスト用クラスを作成し、
テストの関数を作成する。

unittest.TestSuiteを利用するとテストをまとめることができるのかな。
今回はテストが1個だが、利用しておく。

# coding:utf-8
import unittest
from fizzbuzz import fizzbuzz


class TestFizzbuzz(unittest.TestCase):
    def test_15(self):
        self.assertEqual('FizzBuzz', fizzbuzz(15))
        self.assertEqual('FizzBuzz', fizzbuzz(45))

    def test_3(self):
        self.assertEqual('Fizz', fizzbuzz(3))
        self.assertEqual('Fizz', fizzbuzz(12))

    def test_5(self):
        self.assertEqual('Buzz', fizzbuzz(5))
        self.assertEqual('Buzz', fizzbuzz(20))

    def test_x(self):
        self.assertEqual(1, fizzbuzz(1))
        self.assertEqual(4, fizzbuzz(4))


def suite():
    suite = unittest.TestSuite()
    suite.addTests(unittest.makeSuite(TestFizzbuzz))
    return suite
setup.py

パスを通し、テスト対象のsuiteを指定する。

# coding:utf-8
from setuptools import setup, find_packages
import sys
sys.path.append('./src')
sys.path.append('./test')


setup(
    name="Fizzbuzz",
    version="0.1",
    packages=find_packages(),
    test_suite='test_fizzbuzz.suite'
)


これでテストが可能になる。
$ python setup.py test

running test
running egg_info
writing Fizzbuzz.egg-info\PKG-INFO
writing top-level names to Fizzbuzz.egg-info\top_level.txt
writing dependency_links to Fizzbuzz.egg-info\dependency_links.txt
reading manifest file 'Fizzbuzz.egg-info\SOURCES.txt'
writing manifest file 'Fizzbuzz.egg-info\SOURCES.txt'
running build_ext
test_15 (test_fizzbuzz.TestFizzbuzz) ... ok
test_3 (test_fizzbuzz.TestFizzbuzz) ... ok
test_5 (test_fizzbuzz.TestFizzbuzz) ... ok
test_x (test_fizzbuzz.TestFizzbuzz) ... FAIL

======================================================================
FAIL: test_x (test_fizzbuzz.TestFizzbuzz)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "./test\test_fizzbuzz.py", line 20, in test_x
    self.assertEqual(1, fizzbuzz(1))
AssertionError: 1 != 'Buzz'

----------------------------------------------------------------------
Ran 4 tests in 0.003s

FAILED (failures=1)

エラーが出ないようにコードを修正し、pep8やpylintも再度使用して、最終的に以下のようなコードが完成した。

fizzbuzz.py
# coding: utf-8
""" FizzBuzz問題 """


def fizzbuzz(x):
    """ FizzBuzz関数

    引数xが3で割り切れる場合には'Fizz'を、
    引数xが5で割り切れる場合には'Buzz'を、
    引数xが3でも5でも割り切れる場合には'FizzBuzz'を、
    それ以外の場合は引数xを返す
    """
    if x % 15 == 0:
        ans = 'FizzBuzz'
    elif x % 3 == 0:
        ans = 'Fizz'
    elif x % 5 == 0:
        ans = 'Buzz'
    else:
        ans = x
    return ans

$ python setup.py test

running test
running egg_info
writing Fizzbuzz.egg-info\PKG-INFO
writing top-level names to Fizzbuzz.egg-info\top_level.txt
writing dependency_links to Fizzbuzz.egg-info\dependency_links.txt
reading manifest file 'Fizzbuzz.egg-info\SOURCES.txt'
writing manifest file 'Fizzbuzz.egg-info\SOURCES.txt'
running build_ext
test_15 (test_fizzbuzz.TestFizzbuzz) ... ok
test_3 (test_fizzbuzz.TestFizzbuzz) ... ok
test_5 (test_fizzbuzz.TestFizzbuzz) ... ok
test_x (test_fizzbuzz.TestFizzbuzz) ... ok

----------------------------------------------------------------------
Ran 4 tests in 0.003s

OK

今回はpep8やpylintを通してからunittestを通したが、この辺は臨機応変にやったほうが良いか。
あるいは、手順決めておいたほうが良いか。


pylintで8点以上取ろうとするとドキュメンテーションコメントが必要になるので、この程度のコードでも時間がかかる。
その分、品質は上がる気はする。

pep8は慣れれば修正少なく済みそう。

unittestはこれも時間がかかるが、品質は間違いなく上がるはず。
実際、今回コードのバグを防ぐことができた。
(わざとらしい例ではあるが)

何度かやって慣れる必要がありそうだ。