外部API使用でも安心してコードをかくための、mock をつかったテスト

TwitterFacebook に限らず、外部APIを叩かなければならない場面は出てくる。ただでさえテストコードかけないと不安になるのに、外部APIの挙動にあわせてうまく連携しないといけないケースでテストコードがかけないのはかなりこわい。そういうとき、mock が使える。

Mock - Mocking and Testing Library — Mock 1.0.1 documentation
requests と mock を使ってみる - Twisted Mind
python の mock ライブラリを使ってみる: Addicted To Indentation

つかう

テストコードとかテストケースの話になるとモヒカンが飛んできそうなので*1すごくざっくりした理解で書いておく。

from mock import Mock
# モックをつくる
m = Mock()
# m を呼び出すと必ず "hogemoge" がかえってくるようにする
m.return_value = "hogemoge"
# テストする
assert m == "hogemoge"

実際テストコードの記述は unittest や Django の unittest、テストランナーとしては nose なり pytest なり Django test コマンドで走らせたりする。

もうちょっと使う

自分が実装している関数ではなく、外部ライブラリに依存している場合、それの挙動を差し替える

from mock import patch
import some_method
# context_manager で呼び出す
import mymodule
with patch("mymodule.coooooool_script") as m:
    m.return_value = "it's cool"
    self.assertEqual(some_method(), m)
# デコレータで呼び出す
class Tests(TestCase):
    @patch(return_value="it's cool")
    def coooooooool_script_vaild(self, m):
        self.assertEqual(some_method(), m)
# 例外を起こしたい
def coooooooool_script_vaild_valid(self)
    with patch("mymodule.coooooool_script") as m:
        m.side_effect = CoooolError()
        with assertRaises(CoooolError):
            some_method()
# インスタンスメソッドを書き換えたい
# なのでクラスもモックにする必要がある
import MyCoooooolClass
patch('MyCoooooolClass')
def coooooooool_script_vaild_valid_valid(self, mock_class):
    with patch("mycoooooolclass.coooooool_script") as m:
        instance = mock_class.return_value
        instance.soooomeeeeeeeethod.return_value = "hai"
        self.assertEqual(instance.soooomeeeeeeeethod, "hai")

いろいろ記述方法やつかいかた、あるいはテストコード自体に疑問がわいたりもするけど、参考資料みながらこんな感じでかいていた。もっといいやりかたがあるとうれしい。

まあ

しかし「モック便利でなんでもできるけどモック職人になったらそれはそれで考えたほうがいい」というのもあるし、テストしやすい設計だいじだなー。

*1:指摘自体は受けたいけど、前置きしておかないとちょっとこわい