nikkie-ftnextの日記

イベントレポートや読書メモを発信

hynekさんによる記事「“Don’t Mock What You Don’t Own” in 5 Minutes」読んだよメモ 〜モックについて私なんにも分かってなかったな〜

はじめに

昴、お誕生日おめでとう! 今日は劇場で野球しようぜ! nikkieです。

気になっていた積み記事「“Don’t Mock What You Don’t Own” in 5 Minutes」を読みました。

目次

まとめ:「“Don’t Mock What You Don’t Own” in 5 Minutes」とは

  • モック主義TDDが主張する「Don’t Mock What You Don’t Own」の解説記事
  • 例えば、HTTPライブラリを使う実装のテストでモックを使うときは、HTTPライブラリをラップする薄いレイヤーを自作して、それをモックしよう
    • 1つのモックで済み、テストのモックの設定でもビジネスロジックが簡潔に表現される
    • HTTPライブラリのクライアント(=What You Don’t Own)をモックするのは、3つ4つとモックを使うことになり、テストコードでビジネスロジックの表現が分かりにくい

きっかけはちょうぜつ本

ちょうぜつ本の読書会、前回は6章の前半でした。

読書会の中でモックについて、「私はよく分かっていないんですが、こういう意見もあるみたいです」と記事「“Don’t Mock What You Don’t Own” in 5 Minutes」を紹介しました。
読書会の中では参加者の方から「記事の気持ちが分かってきた」というアウトプットがあり、それを聞く中で私も気付きがあって、以下をツイート(ありがとうございました!)

すると、ちょうぜつ本著者のひさてるさんよりリプライをいただきました!
ありがとうございます。

リプライまでいただいたのですが、私が元記事をエアプ同然だったので、(1日1エントリを使って)まずは元記事を理解することにしました。

“Don’t Mock What You Don’t Own” in 5 Minutes

この記事は「Don’t Mock What You Don’t Own」(意訳 所有していないものをモックするな)の解説記事です。
5分で分かる「Don’t Mock What You Don’t Own」 といった趣でしょうか。

EuroPython 2022でLT版が存在します。

このLT、リアルタイムで見ていたのですが、仕事終わりの参加1で朦朧とする意識の中、主張が全然分からなかったけど、引っかかるLTとなりました。
主張が分からないというのは、私は普段(TDDで)テストコードを書く中で、記事やLTでDon'tと言われたモックの仕方を「当然こうやるでしょ」と思ってやっていたからです

Don’t Mock What You Don’t Own とは

今回記事を読んで初めて知ったのは、「Don’t Mock What You Don’t Own」はロンドン学派(=モック主義TDD)が提唱していたということです。
記事を書いたhynekさんは、これを解説していたわけですね(初見の主張だったので、ここから分かっていなかった)

記事では例を挙げて「Don’t Mock What You Don’t Own」が解説されているにすぎません。

例:Docker Repository Client

HTTP通信のライブラリを使ったPythonプログラムの例です2
ライブラリにはhttpxが選択されています。
ビジネスロジックであるget_repos_w_tags関数を実装しています。

def get_repos_w_tags(client):
    # リポジトリ一覧を取得
    # 個々のリポジトリについてタグの一覧を取得

Rude Mocking(しない方がよい🙅‍♂️)

get_repos_w_tags関数の引数にhttpx.Clientのモックを渡します。
httpx.Clientは利用しているだけで、所有しているわけではないですよね。

    client = Mock(
        spec_set=httpx.Client,
        get=Mock(
            return_value=Mock(
                spec_set=httpx.Response,
                json=lambda: {
                    "repositories": []
                },
            )
        ),
    )

hynekさんがこれに否定的な理由は、client.get().json()というビジネスロジックの表現が3つのモックになっていて分かりづらいためと理解しました。
こうなってくるとモックを作るヘルパーを書きたくなりますが、それでは解消しないと書かれています(Mock Hellの文字が...)。

Polite Mocking(こうしよう!🙆‍♂️)

別の方向性の解決策として、HTTPライブラリ(ここではhttpx)をラップするレイヤーを追加する方法が推奨されます。

add a very thin layer around the HTTP library, which becomes the façade between your clean code and the messy outside world.

DockerRegistryClientクラスを爆誕させ、2つのメソッドを生やします。

  • get_repos
  • get_repo_tags

初期化時にhttpx.Clientインスタンスが渡るので、APIアクセスはそれを使った実装です。
DockerRegistryClientを持っている=APIを所有している、と理解しました。
この自作クライアントを引数に渡すとして、get_repos_w_tagsはスッキリ書けます。

このとき、テストでは(httpx.Clientではなく)DockerRegistryClientをモックします

    drc = Mock(
        spec_set=DockerRegistryClient,
        get_repos=lambda: []
    )

Rudeな例との違いはモックが1つで済むこと!
これはdrc.get_repos()というビジネスロジックの実装が分かりやすいです。
get_reposと同様にget_repo_tagsもモックできます。

終わりに

「“Don’t Mock What You Don’t Own” in 5 Minutes」、やっと意味分かった〜!
ここはいつもの天使様の登場ですね。

所有していないものをモックするTDDとか舐めているのですか

ごめんなさい〜。
練習していこうと思います。

hynekさんの記事は情報量がすごくて、新しく知ったものがいくつもありました。
アウトプットしていただいたことに感謝です


  1. 日本との時差は9時間。働いた後に興味あるセッションを見ていました
  2. Don’t Mock What You Don’t OwnもHTTPライブラリが例です