tl;dr;
- unittestはPythonが公式に用意しているユニットテストフレームワーク
- 初めてテストを使ったが、大規模なコードになればなるほどテストが重要だと思うので、今後は、並行してテストを書いていくようにしたい。
- testを先に書いてから開発をする手法もあるらしい DRR?
- テストはテストするmoduleとディレクトリを分ける
- argumentを渡すようにすることで、argparaseをテストすることもできる
official ref.
https://docs.python.org/3/library/unittest.html
Things learned
1. test moduleの場所について
unittestはテストしたいmodule内に直接記載するケースもあるが分けるケースが多い。それは以下のような理由からである。(公式Ref抜粋)
そのため今回はtestsフォルダを直下に作り,そこにtest.pyを作成した
- The test module can be run standalone from the command line.
- The test code can more easily be separated from shipped code.
- There is less temptation to change test code to fit the code it tests without a good reason.
- Test code should be modified much less frequently than the code it tests.
- Tested code can be refactored more easily.
- Tests for modules written in C must be in separate modules anyway, so why not be consistent?
- If the testing strategy changes, there is no need to change the source code.
2. pathについて
上記のようにテストしたいmoduleと異なる階層(1つ子階層)にtests/tet.py
としたため, moduleをimportするのに一工夫必要。通常import
は同階層、およびglobal moduleのみを検索するため、階層が異なる場合は、検索する場所をpath
, os
を使って指定する必要がある。今回は以下のようにした。
import os, sys, unittest
root_folder = os.path.abspath(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
sys.path.append(root_folder)
中身を見てみると
os.path.abspath(__file__)
はこのmoduleのabspathを示す。
os.path.dirname(os.path.abspath(__file__))
はmoduleのあるディレクトリ名
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
はさらに一つ親階層
で、sys
に渡すことで、root_folder
階層を検索することができる。
3. subTest
関数内でif - else
文などを用いている場合、各条件についてテストをしたかったりする。一つ一つ条件を直接指定してテストすることも可能だが、subTest
を使うと、テスト内でfor
ループを使うことができる。かつ、この場合、ループ内でテストがFailed
となっても、他の場合をテストし続けることができる。
def test_sort_option_is_valid(self):
for i in range(1, 5):
with self.subTest(i=i):
self.assertIn(SortType(i), SortType)
4. argsのテスト
CLI, argumentsを利用するアプリケーションの場合、argumentsにて正しいデータが取得できているかをテストしたい。その場合は、argsにargumentsを渡すようにしてやることで、テストできる。
def main():
args = get_args(sys.argv[1:])
def get_args(args):
parser = argparse.ArgumentParser(description='Sort some integers.')
parser.add_argument( ...
def test_parser_input_file(self):
args = get_args(['-i', './tests/test_input.txt'])
self.assertEqual(args.input.name, './tests/test_input.txt')
これにて取得しているargsが意図したものかをテストすることができる。