Pythonテスト超初心者の第一歩
ソフトウェア開発を行うエンジニアの発信する情報や様子を見聞きしている中で、頭の中に残ってなんとなく気になる「テスト」という言葉。
「プログラマ・エンジニアとして活躍していくためにはテストもできるようにならないといけないのかな」と思うものの、実際テストとは何なのかイメージがつきませんよね。
特に独学でプログラミングを学んでいる場合、テストについて学んだり実践したりする機会はありません。自己流でなんとかプログラムを動かしている、という場合も多いのではないでしょうか。
そこで本記事では、Python初心者の方が初めてテストに触れる、その第一歩を後押ししたいと思います。
テストとはどのようなものなのか。大まかな全体像を簡単に説明します。
また、Python付属ライブラリのunittestを使ってごく簡単なテストを行ってみましょう。まずは体験してみるのが手っ取り早いですよね。
▽Pythonに関する記事はこちら
テストって?
そもそもソフトウェア開発におけるテストとはどのようなものなのでしょうか。
大まかにいうと「想定している機能を十全に実現できているか、さまざまな場面で意図しない挙動を起こさないか、ソフトウェアに問題は無いかをチェックすること」です。
チェックのためにさまざまな処理を行い、結果が期待通りかどうかを確認します。
その際、「正しい使い方で正しい結果が返ってくるかどうか」だけでなく、「間違った使い方をしっかりと処理できるかどうか」についてもテストしておく必要があります。
例えば、「入力x, yに対してx÷yの結果を返す」というプログラムについて、
「x = 10, y = 2 を入力して5が返ってくるか」だけでなく、
「x = 1, y = 0 を入力して『yは0以外を入力してください』と表示され、再度yの入力待ちになるかどうか(問題のある入力も見逃さず、適切に処理できているか)」といったことも確認が必要です。
テストの結果バグを見つけることができれば、そのバグを修正して再度テストを行います。
バグを発見して潰していくことでソフトウェアの品質を向上させ、致命的な問題の発生を防ぎます。
また、テストの入力や出力、その際の環境等の情報を記録しておくことで開発者自身も自分の製品の品質に自信を持つことができます。
勘で開発を行っていても出来上がったものの品質には限界があります。人はミスを犯すものです。
適切なテストを行うことで、少なくともテストを行った処理については「問題ない」と考えられます。テストの結果を残しておくことで品質の依代・証拠にもなるのです。
自分が書いたコードがさまざまな状況で問題なく意図したとおりに動くかどうか、ある程度の自信を持って世間に公表するためにもテストは重要です。
加えて、テストは実現したい機能を反映しているものでもあるため、コードの内容・機能を知るための資料にもなります。
このように、テストは出来上がったソフトウェアがリリースされる前にバグを見つけ出し、世の中に出しても問題なく役目を果たせるようにするために重要です。
テストって面倒くさそうだけど…
テストって、なんだか面倒くさそうですよね。
テスト用のコードや記録を書く必要もあるため、追加で時間がかかるというデメリットもあります。テストする時間があるなら開発を進めたい、と思うものでしょう。
しかし、着実にテストを行うことは「テストで問題なかったのだからこの機能は問題ない」という「安定した足場」を作りつつソフトウェア開発を進めていくことに繋がります。
土台がぐらついていると、上に何かを積み重ねたとしてもいつか崩れてしまいますよね。そして、崩れてから問題のあった部分を探すのは大変です。
テストを行わないまま開発を進め、プログラム完成間近に問題が発生した場合の方が、バグの原因を探し出して潰すのにかかる時間が膨大になる可能性も大きいです。
「後々のデバッグの時間を短縮するためにテストを行う」と考えると、適宜テストを行う方が効率的と言えるかもしれません。
テストって難しそうだけど…
テストの重要性は分かりましたが、そんな重大な役目を担うテストって、なんだか難しそうですよね。今まで触れたことのないものは難しそうに思えます。
そんなPythonテスト初心者の方は、まずは小さいテストに触れてみましょう。
開発の流れの中では、
・単体テスト(以下「ユニットテスト」)
・統合テスト
・システムテスト
・受け入れテスト
などといったように、小さな単位でのテストから大きな単位でのテストへと進んでいきます。
関数やメソッドといった小さい部品単位でのテストから始め、「問題なし」と確認できたものを組み合わせていくことで手戻りがなくなります。開発者自身も自信を持って開発を進められます。
この最小単位のテストがユニットテストです。
Pythonのunittestを使ってみよう
テストに触れたことが無い場合、「興味はあるけど難しそう」と思うでしょう。しかし、未経験のことは難しく思えるもの。いきなり難しいことをするのではなく、まずは小さいテストを体験してみましょう。
ありがたいことに、Pythonには「unittest」というユニットテストを行うライブラリが標準的に付属しています。
以下ではこのunittestを使ってみます。ぜひ自分の環境で実際にコードを実行してみてください。
※ちなみにコードを実行した際の環境はWindows10(64bit), python3.5です。
unittestの第一歩
まず、テストしたい関数を記述した.pyファイルを保存します。
今回は引数を2乗して返す以下の関数「square」をテストします。
# ファイル名はsquare.py def square(a): return a**2
テストのための.pyファイルも保存します。
# ファイル名はtest_square.py import unittest # unittestをインポート from square import square # テストする関数をインポート class TestMysquare(unittest.TestCase): # unittest.TestCaseを継承したクラスを作成 def test_square(self): # テスト用のメソッド名は`test_`で始める self.assertEqual(square(3), 9) # square関数に3を渡すと9が出力されるかどうか確認 self.assertEqual(square(1), 1) # square関数に1を渡すと1が出力されるかどうか確認 self.assertEqual(square(-3), 9) # square関数に-3を渡すと9が出力されるかどうか確認 self.assertEqual(square(0), 0) # square関数に0を渡すと0が出力されるかどうか確認 if __name__ == "__main__": unittest.main()
これら2つの.pyファイルを保存後、コンソール上で
「python test_square.py」
を実行するとテスト結果が以下のように表示されます。
. ---------------------------------------------------------------------- Ran 1 test in 0.001s OK
関数の返り値(例:3)と想定していた返り値(例:9)が一致していることが確認できます。
なお、square関数にミスがあった場合、以下のようになります。
# ファイル名はsquare2.py def square2(a): return a*2 # 2乗ではなくただの掛け算になっている(ミス)
# ファイル名はtest_square2.py import unittest # unittestをインポート from square2 import square2 # テストする関数をインポート class TestMysquare(unittest.TestCase): def test_square2(self): self.assertEqual(square2(3), 9) # square関数に3を渡すと9が出力されるかどうか確認 self.assertEqual(square2(1), 1) # square関数に1を渡すと1が出力されるかどうか確認 self.assertEqual(square2(-3), 9) # square関数に-3を渡すと9が出力されるかどうか確認 self.assertEqual(square2(0), 0) # square関数に0を渡すと0が出力されるかどうか確認 if __name__ == "__main__": unittest.main()
コンソール上で
「python test_square2.py」
を実行すると、
====================================================================== FAIL: test_square2 (__main__.TestMysquare) ---------------------------------------------------------------------- Traceback (most recent call last): File"c:/User/test_square2.py", line 6, in test_square2 self.assertEqual(square2(3), 9) AssertionError: 6 != 9 ---------------------------------------------------------------------- Ran 1 test in 0.003s FAILED (failures=1)
「AssertionError: 6 != 9」と表示されます。
関数square2に3を渡したときの返り値が6で、想定していた9と異なることが分かります。
unittestには他にも、
・各テストで共通して行う前処理・後処理をまとめて記述してテストを実行(「setUp」メソッド、「tearDown」メソッド)
・テスト対象の関数が呼び出す他の関数等の値を仮置きしてテストを実行(モックオブジェクト)
など、さまざまな便利機能があります。
今回はごく単純な例でPythonのテストに触れてみました。より詳しくテストについて知りたければ、公式ドキュメント等で情報収集してみましょう。
https://docs.python.jp/3/library/unittest.html
今回はテスト入門の第一歩
ソフトウェア開発におけるテストとはどのようなものか、Pythonでのテストの第一歩について解説しました。
初めの一歩は腰が重いものです。気になりつつも、手を出せないまま時間だけが経っていってしまいます。
しかし、簡単なものであっても一度体験しておけばさらに詳しく学びたくなったときにも手を出しやすいですよね。
なお、「まずは一歩を踏み出すこと」を応援することが目的でしたので、本記事で紹介できたのはテストのほんのさわりの部分です。テストにはさまざまな手法、考え方、実践上のノウハウなどがあります。
本記事がテストについての学びや実践の足がかりとなれば幸いです。
ちなみに、Pythonでテストを行うためのライブラリはunittest以外にも用意されています。こちらの記事もご覧ください。
参考:https://www.capa.co.jp/archives/15126
▼キャパの公式Twitter・FacebookではITに関する情報を随時更新しています!