Python学習【365日チャレンジ!】202日目のマスターU(@Udemy11)です。
突然ですが、きゅうりって収穫せずにほっておくとどうなるか知ってますか?
畑の土が肥えているかどうかにもよりますが、めちゃくちゃ大きくなってウリみたいになるんです。
色は黄色になるし、中身はスカスカの状態になるので、捨てるしかないかなと思って【育ちすぎたきゅうり】とネットで調べてみると育ちすぎたキュウリの料理がたくさんヒットしました。
意外にも、キュウリを収穫し忘れてウリみたいになってしまうという私と同じ失敗をしてしまっている人が多くてびっくりしました。
ま〜、問題なく食べられるみたいなので、ネットの情報を参考に美味しくいただこうと思います。
それでは、今日もPython学習をはじめましょう。
昨日までの復習
昨日まで外部サービスの値を擬似的に取得できるmock
について学習してきました。
MagicMockを使ったり、デコレーターを使ったり、メインコードのメソッドをmockにしたり、クラスごとmockにしたりしていろいろなテストのやり方がありました。
学習した内容は、mock
の一部ですので、もっとたくさんの使い方がありますが、実践しながら使うものを身につけていくようにしましょう。
今日は、メインのコードをどこまでmock
にするのかについて考えてみましょう。
mockの適用範囲
mock
は外部サービスで取得する値を外部のサービスを使わずに擬似的に値を入れてメインコードをテストするものです。
なので、基本的にメインのファイルで完結できる関数については、mock
を使う必要はありませんが、mock
を適用して値を入れることもできます。
この範囲をどこまで適用してもいいのかということが気になるところです。
いっそのこと、全てmock
を適用して、擬似的な値を入れれば、テストコードを書くのが非常に楽になります。
とはいっても、関数が動作しているのか確認するためには、きちんとテストする必要がありますので、何でもかんでもmock
にしてもいいわけではありません。
例えば、銀行などのシステムであれば、ちょっとしたバグでも大問題になってしまいますので、mock
で擬似的な値を入れるのではなく、きちんとしたテストを実行する必要があります。
一方で、自分が開発した簡単なアプリなどでは、バグが見つかれば、その都度修正すれば問題はありませんので、全てmockにしてしまっても問題ないかもしれません。
どんなシステムやアプリを開発するのか、クライアントから受注したものなのかなど、状況によってmock
の適用可能な範囲が変わってきます。
クライアントから、外部サービスだけmock
を使うとか、外部サービスの中でもこの関数だけmock
を使っても構わないなどの要求があるので、その要求に従ってテストを実行することが求められるということです。
クライアントの要求に答えるには、モックオブジェクトライブラリを熟知する必要がありますが、その数は莫大なのですべてを網羅することはできません。
すべてを網羅するのではなく、基本的なmock
の使い方を理解した上で、必要に応じてモックオブジェクトライブラリを活用するのがベストな方法と言えるでしょう。
ファイル内関数をmockしてみる
ということで、メインのファイル内で処理可能な関数をmock
にするコードを書いてみましょう。
これまで使っていたメインのファイルに下記の関数を付け加えて、テストファイルでmock
にしてみます。
def special_bonus(self):
return 0
def cal_salary(self):
try:
bonus = self.bonus_api.bonus_price(year=self.year)
except ConnectionRefusedError:
bonus = 0
bonus += self.special_bonus()
return self.base + bonus
13行目、14行目で新しい関数special_bonus
で特別ボーナスを与える関数を追加しました。
cal_salary
には、21行目でspecial_bonus
を加えた金額を出すように変更しています。
次にテストファイルにコードを付け加えます。
test_salary.py
不要なコードは削除して、Salary
クラスのspecial_bonus
をmock
にするコードと、salary_price
がmock
に入れた値を合計した値になるように160
に変更しました。
import unittest
from unittest import mock
import salary
class TestSalary(unittest.TestCase):
@mock.patch('salary.BonusApi', spec=True)
@mock.patch('salary.Salary.special_bonus')
def test_cal_salary_class(self, mock_special, MockRest):
mock_rest = MockRest.return_value
mock_special.return_value = 10
mock_rest.bonus_price.return_value = 100
s = salary.Salary(year=2020)
salary_price = s.cal_salary()
self.assertEqual(salary_price, 160)
mock_rest.bonus_price.assert_called()
10行目の関数に2つのmock
の引数を入れていますが、デコレーターを2つ入れた場合は、関数の引数は、あとのほうが第一引数になります。
あとはこれまでと同じように、return.value
で値を入れて、18行目でbase
の50
とbonus
の100
とspecial_bonus
の10
を足した160
を入れてassertEqual
で確認しています。
テストをを実行すれば、スペルミスなどがなければテストにパスします。
終わりがない
Pythonの学習は、ここまでやれば終了というようなものではなく、終わりがありません。
スポーツや学問も目標はあっても終わりはありません。
目標を達成できれば、不足しているところが見えてくるので、さらに学習することが出てくるわけです。
完璧を目指すのではなく、満足のいく結果を出すために、現時点でできることをやることが大切です。
Pythonのドキュメントが完璧に頭の中に入れるのは非効率ですので、必要になったときにどこから引き出したらいいのか分かればいいわけですし、使用頻度が低いコードを覚えるのは、有限であるわたしたちのリソースを無駄遣いしているのと同じです。
公式ドキュメントを辞書代わりに使って、使用頻度の高いものをしっかりと覚えるようにしましょう。
それでは明日もGood Python!