Python mockのspecの使い方

Python学習【365日チャレンジ!】201日目のマスターU(@Udemy11)です。

朝晩が随分過ごしやすくなってきたので、釣りに行きたい欲求がふつふつと湧いてきています。

と言いつつ、早朝から海にでかけてしまっているのですが、なかなか釣果に恵まれないんですよね。

釣れても1匹とかなので、ほんとがっくりして、また釣りに行きたくなるんです。

天候もいい感じになってきているので、そろそろ爆釣してもいい頃なんですが、いつになればそんなおいしい目をみられるんでしょうか?

それでは、今日もPython学習をはじめましょう。

昨日の復習

昨日は、side_effectで例外処理やリストを使った使い方を学習しました。

side_effectを使ってサーバーにアクセスできないConnectionRefusedErrorなどもテストで処理ができるので、結果を参考にして、メインファイルにバグがないように作り変えていくこともできました。

また、リストを使って外部サービスを複数回呼び出したテストを実行したりできました。

詳細については、こちらの記事をごらんください。

今日は、クラスをまるごとmock化することができるspecを学習します。

salary.py

メインファイル(salary.py)は、昨日エラーコードの対応を追加したファイルを使います。

test_cal_salary.py

テストファイルは、デコレーターを使ってメインファイルのクラスをまるごとmock化したコードにします。

import unittest
from unittest import mock

import salary


class TestSalary(unittest.TestCase):
    @mock.patch('salary.BonusApi', spec=True)
    def test_cal_salary_class(self, MockRest):
        mock_rest = MockRest.return_value
        mock_rest.bonus_price.return_value = 100

        s = salary.Salary(year=2020)
        salary_price = s.cal_salary()

        self.assertEqual(salary_price, 150)
        mock_rest.bonus_price.assert_called()

8行目でメインファイル(salary.py)のBonusApiクラスを指定して、引数specTrueを代入します。

9行目のテスト関数には、クラスなのでキャメルケースで変数MockRestを指定します。

10行目は、mock_rest = MockRest()とも記述できますが、公式ドキュメントには上記の書き方でオブジェクトを生成する方法が推奨されています。

11行目でbonus_priceの返り値に100を代入して、その後のコードはこれまで同様で、最後にbonus_priceが呼ばれたかどうかをテストしています。

関数を追加して確認

クラス全体がmock化されているか確認するために、メインファイル(salary.py)とtest_salary.pyにコードを付け足します。

salary.py

class BonusApi(object):
    def check_mock(self):
        return 'Clear'
    def bonus_price(self, year):
        req = requests.get('http://localhost/bonus', params={'year': year})
        return req.json()['price']

test_salary.py

        mock_rest.check_mock.return_value = 'Yes!'

メインのファイルには簡単なClear!を返すcheck_mockという関数を追記して、テストファイルには、check_mockYes!を代入してテストしています。

Python mockのspecの使い方

スペルミスなどがなければテストにパスします。

これまで何度もテストを実行してきましたが、うまくいかないときは、恥ずかしながらスペルミスが原因でした。

講座の内容をたどりながらコードを書いているので、まずはスペルミスがないか確認しましょう。

また、あまり意味がありませんが、テストで代入したYes!check_mockに入っているかどうかを確認するには、次のコードを追加します。

        c_mock = mock_rest.check_mock.return_value
        self.assertEqual(c_mock, 'Yes!')

Yes!を入れたcheck_mockを変数c_mockに代入して、それがYes!かどうかをチェックしています。

ほんと意味があるのかないのかよくわかりませんが、このコードを追加してもテストはパスします。

試してみることは大切

これまでも何度も自分に言い聞かせていますが、試すことが非常に重要です。

酒井さんの講座の内容をそのまま写経することは勉強にはなりますが、そこから自分なりのコードを書いてみることで学習した内容の理解が深まります。

Udemy講師 酒井さん プログラミング Python

講座を受講したり、写経するだけではわからなかったことも自分なりに考えながら、試してみることでわからなかったことがわかったり、こんなこともできるのかと気づいたりできるので、あえてこんなことする人はいないだろうと思うことを試してみるとおもしろいかもしれませんよ。

ぜひ、いろいろなコードを試して失敗してみてください。

それでは、明日もGood Python!