単純なスクリプトのユニットテストを書いていて躓くことがあったので、解決法をメモ。
例えばこんな、おみくじをするだけの簡単なスクリプトがあるとする。
import random fortunes = { 1: '凶', 2: '吉', 3: '大吉' } number = random.choice([1, 2, 3]) fortune = fortunes[number] print(fortune)
変数number
に1〜3までのランダムな値が入って、対応する運勢が出力される 。random.choice()
の部分をパッチして、期待通りの運勢が出力されることをテストする。
import unittest from importlib import import_module from unittest.mock import patch class FortuneTestCase(unittest.TestCase): @patch('random.choice', lambda x: 1) def test_bad(self): module = import_module('fortune') expect = ('凶') self.assertEqual(module.fortune, expect) @patch('random.choice', lambda x: 3) def test_good(self): module = import_module('fortune') expect = ('大吉') self.assertEqual(module.fortune, expect)
さて、このテストを実行するとどうなるか。
(env) $ python3 -m unittest tests.test 凶 .F ====================================================================== FAIL: test_good (tests.test.FortuneTestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/mock.py", line 1191, in patched return func(*args, **keywargs) File "/tests/test.py", line 21, in test_good self.assertEqual(module.fortune, expect) AssertionError: '凶' != '大吉' - 凶 + 大吉 ---------------------------------------------------------------------- Ran 2 tests in 0.029s FAILED (failures=1)
test_good
メソッドのアサーションで例外が発生する。パッチした値が反映されていない。つまり初めに実行されたtest_bad
メソッドでパッチした値がそのまま使われている。
値がパッチされっぱなしなのではれば、モジュールを読み込み直してみる。importlib.reload()
を使う。
Python3 公式ドキュメント importlib - reload
import unittest from importlib import import_module, reload from unittest.mock import patch class FortuneTestCase(unittest.TestCase): @patch('random.choice', lambda x: 1) def test_bad(self): module = import_module('fortune') expect = ('凶') self.assertEqual(module.fortune, expect) @patch('random.choice', lambda x: 3) def test_good(self): module = import_module('fortune') reload(module) expect = ('大吉') self.assertEqual(module.fortune, expect)
(env) $ python3 -m unittest tests.test 凶 凶 .大吉 . ---------------------------------------------------------------------- Ran 2 tests in 0.025s OK
無事テストが通った。テストメソッド実行ごとにパッチされると思っていただけにこの動きにだいぶ悩むこととなった。
公式ドキュメントに以下の注釈があるためtest_good
メソッドでだけreload
を使った。
注釈 :いろいろなテストが実行される順序は、文字列の組み込みの順序でテストメソッド名をソートすることで決まります。
https://docs.python.jp/3/library/unittest.html#organizing-test-code