クリーム色の背景、色付き表示、高コントラスト、大きな文字のほうが読みやすいですか?配色と文字を選ぶと、すべてのページに一度で反映されます。

無料レッスン

モックとスタブでテストする

このレッスンのあとで: 時刻や乱数に頼る関数を、偽物に替えてテストすることができる。

55分のレッスンコマンド6件毎日使う Linux コマンドのラボ:エンジニアが暗記している30のコマンド

モックとスタブでテストする

テストダブルとは、本物の代わりにテストで使う偽物の部品の総称で、スタブやモックがその仲間です。時計や乱数のように毎回変わるものに頼る関数も、偽物を使えば毎回同じ結果でテストできます。

朝に動かすと合格し、夜に動かすと落ちるテストは、信じられません。偽物は、それを防ぎます。

このレッスンのゴール

メモ

時刻や乱数に頼る関数を、偽物に替えてテストすることができる。

キーワード

ことばいみれい
依存関数が頼っている、外の部品時計、乱数
テストダブルテストで本物の代わりに使う偽物の総称
スタブ決めた値を返すだけの偽物
モック呼ばれ方を記録する偽物
注入偽物を引数として、関数に渡すこと

学生が入力するセルは、このプレビューでは空白です。

毎回変わる値に頼るとテストが揺れる

挨拶の関数が、いまの時刻を時計から読むとします。朝に動かすと「おはよう」、夜に動かすと「こんばんは」が出ます。

同じテストが、動かす時刻で合格したり落ちたりします。乱数も同じです。

期待値を決められないテストは、役に立ちません。

例: 頼る部品と偽物

頼る部品毎回変わる理由偽物に替えると
時計動かす時刻が毎回違う決めた時刻を返す
乱数実行ごとに違う数が出る決めた数を返す
ネットワーク遅い、つながらないことがある決めた応答を返す

学生が入力するセルは、このプレビューでは空白です。

決めた時刻を返す偽物

コード: python
def greeting(get_hour):
    hour = get_hour()
    if hour < 12:
        return "おはようございます"
    if hour < 18:
        return "こんにちは"
    return "こんばんは"

# 本物の時計の代わりに、決めた時刻を返す偽物を作ります。
def fake_morning():
    return 9

print(greeting(fake_morning))
# fake_morning が 20 を返すように変えて、もう一度実行しましょう。

偽物は引数で渡す

時計を関数の中で直接読むと、替えられません。時計を読む関数を、引数として受け取るようにします。これが注入です。

本番では本物の時計を渡し、テストでは決めた時刻を返す偽物を渡します。関数の中身は、1行も変わりません。

作業用のフォルダを作る

コマンド
mkdir -p ~/testing/fakes

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

そのフォルダへ移る

コマンド
cd ~/testing/fakes

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

時計を受け取る関数を書く

コマンド
printf 'def greeting(get_hour):\n    hour = get_hour()\n    if hour < 12:\n        return "good morning"\n    if hour < 18:\n        return "good afternoon"\n    return "good evening"\n' > greet.py

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

3つの偽物でテストを書く

コマンド
printf 'import unittest\nfrom greet import greeting\n\n\ndef fake_nine():\n    return 9\n\n\ndef fake_noon():\n    return 12\n\n\ndef fake_night():\n    return 20\n\n\nclass TestGreeting(unittest.TestCase):\n    def test_morning(self):\n        self.assertEqual(greeting(fake_nine), "good morning")\n\n    def test_noon(self):\n        self.assertEqual(greeting(fake_noon), "good afternoon")\n\n    def test_night(self):\n        self.assertEqual(greeting(fake_night), "good evening")\n' > test_greet.py

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

テストを動かす

コマンド
python3 -m unittest 2>&1 | tail -n 1

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

いつ動かしても OK になる

最後の行は OK です。この結果は、朝に動かしても夜に動かしても変わりません。

偽物は、9時、12時、20時の3つを用意しました。12時は、午前と午後の境目です。前のレッスンで学んだ境界値を、ここでも使っています。

ラボで作るファイルは、英語の文字だけで書いています。

例: スタブとモックの違い

偽物することは確かめること
スタブ決めた値を返す関数の結果
モック呼ばれ方を記録する何回、どの引数で呼ばれたか

学生が入力するセルは、このプレビューでは空白です。

呼ばれ方を記録する偽物

コード: python
calls = []

def pick_menu(menus, roll):
    if len(menus) == 0:
        return ""
    return menus[roll(len(menus))]

# 決めた番号を返し、呼ばれたときの引数を記録する偽物です。
def fake_roll(n):
    calls.append(n)
    return 1

print(pick_menu(["カレー", "うどん", "ラーメン"], fake_roll))
print(calls)
# fake_roll が返す数を 2 に変えて、もう一度実行しましょう。

Python には偽物の道具もある

標準ライブラリの unittest.mock に、偽物を作る道具があります。Mock(return_value=9) は、呼ぶと 9 を返し、呼ばれた回数も覚えます。

次の1行は、偽物を1回呼んで、返した値と回数を出します。出力は 9 1 です。

標準の Mock を呼ぶ

コマンド
python3 -c "from unittest import mock; m = mock.Mock(return_value=9); print(m(), m.call_count)"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

よくある間違い

注意

「何でも偽物にすれば、テストは速くて安全だ」と考える人が多くいます。偽物が本物と違う動きをすると、テストは通ってもバグが残ります。偽物にするのは、遅い、不安定、危険な部品だけにしましょう。

このラボとソースについて: 毎日使う Linux コマンドのラボ:エンジニアが暗記している30のコマンド

授業で使いますか?このモジュールを学生に割り当てると、教員用ガイド、自動採点つきの課題、学生ごとのラボを使えます。