より良いエンジニアを目指して

1日1つ。良くなる!上手くなる!

HttpClientのモックとなるライブラリRichardSzalay.MockHttp

単体テストコードを書くことを考えると、外部のAPIにアクセスするような処理をMockに出来ないかなーとは思ってました。

私の場合はインターフェースを切ってました。

f:id:rimever:20190328074957p:plain

下記のライブラリを見つけました。

github.com

HttpClientクラスの代わりに処理するMockHttpClientクラスを提供します。

.NET Framework4.5にも対応しており、少し前のランタイムでも利用出来るので幅広く使えると思います。

実践

試しにGitHub APIのMockHttpClientを実装してみます。

f:id:rimever:20190328093637p:plain
cmderでcurlしてリクエストとレスポンスを確認しながら実装

GET https://api.github.com/repos/rimever/NLP100Knocks/languages

とした時、以下のようなレスポンスが

{
  "C#": 267389,
  "HTML": 19081,
  "CSS": 1274,
  "JavaScript": 66
}

返却されるケースです。

        /// <summary>
        /// https://developer.github.com/v3/ のMockHttpClientをつくって試します。
        /// </summary>
        [Test]
        public async Task TryMockHttpClient()
        {
            var mockHttp = new MockHttpMessageHandler();
            // example:https://api.github.com/repos/rimever/NLP100Knocks/languages
            mockHttp.When("https://api.github.com/repos/*/*/languages") // user名とリポジトリ名はパターンマッチ
                .Respond("application/json"
                    , $@"{{
  'C#': 267389,
  'HTML': 19081,
  'CSS': 1274,
  'JavaScript': 66,
  'なぞのげんご': 9999
}}");  // Mockなので結果を一部ありえない結果にしてみる

            var client = mockHttp.ToHttpClient();

            var response = await client.GetAsync("https://api.github.com/repos/user_name/repository_name/languages"); // ユーザー名と言語は任意の値でOK

            var json = await response.Content.ReadAsStringAsync();

            Console.Write(json);
        }

問題なく意図した出力されました。

f:id:rimever:20190328083811p:plain

複数のWebAPIの振る舞いをさせる

一つのMockHttpClientに複数のWebAPIの処理を実装することも可能です。

これがナイスな点だと思います。先述した私のオレオレ設計だとWebAPI一つにつき1メソッドになりますので。

            var mockHttp = new MockHttpMessageHandler();
            // example:https://api.github.com/repos/rimever/NLP100Knocks/languages
            mockHttp.When("https://api.github.com/repos/*/*/languages")
                .Respond("application/json"
                    , $@"{{
  'なぞのげんご': 9999
}}");
            mockHttp.When("https://api.github.com/licenses").Respond("application/json"
                , $@"{{
       {{
            'key': 'mit',
            'name': 'MIT License',
            'spdx_id': 'MIT',
            'url': 'https://api.github.com/licenses/mit',
            'node_id': 'MDc6TGljZW5zZW1pdA=='
        }}
}}");

            var client = mockHttp.ToHttpClient();

            Console.WriteLine("リポジトリの言語取得サービスのモックを試します。");
            {
                var response =
                    await client.GetAsync("https://api.github.com/repos/user_name/repository_name/languages");

                var json = await response.Content.ReadAsStringAsync();

                Console.WriteLine(json);
            }
            Console.WriteLine("ライセンス一覧取得サービスのモックを試します。");
            {
                var response =
                    await client.GetAsync("https://api.github.com/licenses");

                var json = await response.Content.ReadAsStringAsync();

                Console.WriteLine(json);
            }

f:id:rimever:20190328090440p:plain