空飛ぶロボットのつくりかた

ロボットをつくるために必要な技術をまとめます。ロボットの未来についても考えたりします。

テストを学ぼう (1) ! ~ユニットテストについて~

ユニットテストってなんでするの?

書いたコードがちゃんと動くか不安だから。

テストを書いておくと安心を得ることができます。安心大事。

また、大胆にリファクタリングだってできちゃう。

以下の考え方はユニットテストが基盤

  1. 継続的テスト
  2. テスト駆動開発
  3. ビヘイビア駆動開発

継続的テストとは

テストを早い段階から(できれば自動的に)回すことが大事。理由は、手戻り、リグレッション(テストに失敗→修正→新たな問題が発生)が発生するからです。

ユニットテストは最小クラスの部品などを対象とするため、早い段階から回すことができます。また、自動化がしやすく、ローコストで繰り返し行うことができます。

こうすることで、修正による影響がすぐにフィードバックされ、安心してリリースができる。

参考:継続的テスト: IBM の見解  継続的テストの利点

テスト駆動開発とは

プロダクションコードを書く前にテストを書く方法。テストファースト。目的が明確になるため、目的にあったすっきりとしたコードがかける。

参考:@IT:特集 「テスト駆動開発」はプログラマのストレスを軽減するか?

ビヘイビア駆動開発とは

仕様を起点とした開発手法。テストファーストに対してスペックファーストという。

pythonでは「behave」という振舞駆動開発ツールがある。

参考: 振る舞い駆動開発入門  ビヘイビア駆動開発 - Wikipedia  Welcome to behave! — behave 1.2.5 documentation  ビヘイビア駆動開発ツール 

ユニットテストを書いてみよう

pythonでunittestを用いるものとする

参考: 26.4. unittest — ユニットテストフレームワーク — Python 3.6.1 ドキュメント Pythonでエラーが発生することをテストする - Misc Notes

以下のプロダクトコードとテストコードを同じフォルダに配置する

プロダクトコード

数値微分をしてみる

以下をnumerical_diff.pyとして保存

# -*- coding:utf-8 -*-                                                          

# 2次関数の数値微分を行うクラス                                                 
class Diff:
    # 2次関数                                                                   
    def f(self,x):
        y = x**2
        return y

    # 数値微分                                                                  
    def numerical_diff(self,f,x,dx):
        if dx == 0:
            raise ValueError('division by zero')
        else:
            x_diff = (f(x+dx)-f(x-dx))/(2*dx)
            return x_diff

if __name__ == '__main__':
    diff = Diff()
    print diff.numerical_diff(diff.f,3,0.001)

テストコード

以下をtest_numerical_diff.pyとして保存

# -*- coding:utf-8 -*-                                                 
import unittest, numerical_diff

class TestDiff(unittest.TestCase):
    # 前処理                                                           
    def setUp(self):
        self.diff = numerical_diff.Diff()

    # 後片付け                                                         
    def tearDowm(self):
        pass

    # 3の2乗のテスト                                                   
    def test_func1(self):
        self.assertEqual(self.diff.f(3),9)

    # 4の2乗のテスト                                                   
    def test_func2(self):
        self.assertEqual(self.diff.f(4),16)

    # 数値微分ができているかのテスト                                   
    def test_numerical_diff(self):
        self.assertAlmostEqual(self.diff.numerical_diff(self.diff.f,3,0.001),6.0, delta=1e-8)
        self.assertEqual(round(self.diff.numerical_diff(self.diff.f,3,0.001)),6.0) # 良くない例

    # 0で割られたときの例外のテスト                                    
    def test_numerical_diff_zero_error(self):
        with self.assertRaises(ValueError) as cm:
            self.diff.numerical_diff(self.diff.f,3,0.0)
        exception = cm.exception
        self.assertEqual(exception.message, 'division by zero')

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

参考:2. 組み込み関数 — Python 3.6.1 ドキュメント

実行方法

以下を上記フォルダ内で実行

python -m unittest test_numerical_diff

もしくはターミナルで

$ ipython
$ run test_numerical_diff.py

実行結果

....
----------------------------------------------------------------------
Ran 4 tests in 0.000s

OK

 今後勉強したいツール

nose