ようへいの日々精進XP

よかろうもん

テストドンブリ(ドリブン)Python FizzBuzz

Python Boot Camp に行ってきた

pyconjp.connpass.com

言語を基礎から学ぶ機会が得られて本当に良かった。

ということで、教材の「2. Pythonをはじめよう」で FizzBuzz のサンプルがあったので UnitTest を使ってテストドリブンで FizzBuzz を実装してみた。

テストドリブン FizzBuzz

ドキュメント

環境

今回は以下のような環境で FizzBuzz する。

$ python -V
Python 3.6.2

FizzBuzz とは

もはや説明不要の泣く子も黙るゲーム。

  • 1 から順に数字を発言する
  • 数字が 3 で割り切れる場合は「Fizz」と発言する
  • 5 で割り切れる場合は「Buzz」と発言する
  • 3 と 5 で割り切れる場合は「FizzBuzz」と発言する

最初のテスト

以下のようなテストを書く。

import unittest


class Testfizzbuzz(unittest.TestCase):

    def test_should_return_normal_number(self):
        self.assertEqual(fizzbuzz(1), "1")

    def test_3_should_return_Fizz(self):
        self.assertEqual(fizzbuzz(3), "Fizz")

    def test_5_should_return_Buzz(self):
        self.assertEqual(fizzbuzz(5), "Buzz")

    def test_3_and_5_should_return_FizzBuzz(self):
        self.assertEqual(fizzbuzz(15), "FizzBuzz")


if __name__ == '__main__':
    unittest.main()

テストを走らせてみる。

$ python fizzbuzz_test.py
EEEE
======================================================================
ERROR: test_3_and_5_should_return_FizzBuzz (__main__.Testfizzbuzz)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "fizzbuzz_test.py", line 16, in test_3_and_5_should_return_FizzBuzz
    self.assertEqual(fizzbuzz(15), "FizzBuzz")
NameError: name 'fizzbuzz' is not defined

======================================================================
ERROR: test_3_should_return_Fizz (__main__.Testfizzbuzz)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "fizzbuzz_test.py", line 10, in test_3_should_return_Fizz
    self.assertEqual(fizzbuzz(3), "Fizz")
NameError: name 'fizzbuzz' is not defined

======================================================================
ERROR: test_5_should_return_Buzz (__main__.Testfizzbuzz)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "fizzbuzz_test.py", line 13, in test_5_should_return_Buzz
    self.assertEqual(fizzbuzz(5), "Buzz")
NameError: name 'fizzbuzz' is not defined

======================================================================
ERROR: test_should_return_normal_number (__main__.Testfizzbuzz)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "fizzbuzz_test.py", line 7, in test_should_return_normal_number
    self.assertEqual(fizzbuzz(1), "1")
NameError: name 'fizzbuzz' is not defined

----------------------------------------------------------------------
Ran 4 tests in 0.001s

FAILED (errors=4)

そもそも、fizzbuzz という関数が定義されていないのでテストは失敗どころかエラーになってしまう。

ここからはじめてみる。

fizzbuzz 関数の実装(1)

ファイル名を fizzbuzz.py として、以下のように fizzbuzz 関数を実装してみる。

def fizzbuzz(num):
    return str(num)


for num in range(1, 21):
    print(fizzbuzz(num))

実行すると以下のように出力される。

$ python fizzbuzz.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

テスト(2)

fizzbuzz_test.py を以下のように修正して fizzbuzz 関数を import する。

import unittest
from fizzbuzz import fizzbuzz


class Testfizzbuzz(unittest.TestCase):

    def test_should_return_normal_number(self):
        self.assertEqual(fizzbuzz(1), "1")

... 略 ...

if __name__ == '__main__':
    unittest.main()

改めてテストを実行する。

$ python fizzbuzz_test.py
... 略 ...
FFF.
======================================================================
FAIL: test_3_and_5_should_return_FizzBuzz (__main__.Testfizzbuzz)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "fizzbuzz_test.py", line 17, in test_3_and_5_should_return_FizzBuzz
    self.assertEqual(fizzbuzz(15), "FizzBuzz")
AssertionError: '15' != 'FizzBuzz'
- 15
+ FizzBuzz

... 略 ...

----------------------------------------------------------------------
Ran 4 tests in 0.001s

FAILED (failures=3)

テストが 1 つ成功した。

fizzbuzz 関数の実装(2)

以下のように fizzbuzz 関数に実装を加える。

def fizzbuzz(num):
    if num % 3 == 0 and num % 5 == 0:
        return 'FizzBuzz'
    else:
        return str(num)


for num in range(1, 21):
    print(fizzbuzz(num))

実行すると以下のように出力される。

$ python fizzbuzz.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
FizzBuzz
16
17
18
19
20

テスト(3)

fizzbuzz_test.py を実行してテストする。

$ python fizzbuzz_test.py
... 略 ...
.FF.
======================================================================
FAIL: test_3_should_return_Fizz (__main__.Testfizzbuzz)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "fizzbuzz_test.py", line 11, in test_3_should_return_Fizz
    self.assertEqual(fizzbuzz(3), "Fizz")
AssertionError: '3' != 'Fizz'
- 3
+ Fizz


======================================================================
FAIL: test_5_should_return_Buzz (__main__.Testfizzbuzz)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "fizzbuzz_test.py", line 14, in test_5_should_return_Buzz
    self.assertEqual(fizzbuzz(5), "Buzz")
AssertionError: '5' != 'Buzz'
- 5
+ Buzz


----------------------------------------------------------------------
Ran 4 tests in 0.001s

FAILED (failures=2)

テストが 2 つ成功したぜ。

fizzbuzz 関数の実装(3)

Fizz と Buzz も追加実装してみる。

def fizzbuzz(num):
    if num % 3 == 0 and num % 5 == 0:
        return 'FizzBuzz'
    elif num % 3 == 0:
        return 'Fizz'
    elif num % 5 == 0:
        return 'Buzz'

    else:
        return str(num)


for num in range(1, 21):
    print(fizzbuzz(num))

実行すると以下のように出力される。

$ python fizzbuzz.py
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz

おお。

テスト(4)

改めて fizzbuzz_test.py を実行してテストする。

$ python fizzbuzz_test.py
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
....
----------------------------------------------------------------------
Ran 4 tests in 0.000s

OK

テスト成功!!

以上

すごくシンプルだけどテストドリブンが体験出来た。なんか楽しいなあ。