びぼーろくっ!

誰かに見せるわけでもないけど、備忘録として。。

MockHttpでHttpResponseをモック化!

最近C#ばっかりです。
なかなか型の制約とかHttpContextがHttpContextBaseを継承してなかったりとか色々としんどいです。

今回戸惑ったのがAPIのRequest⇔ResponseをMock化したかったのですが
NUGET辺りで探してたらいいのがありました。

これです。
github.com


今回はNUnitFrameWorkを使用しています。

using Newtonsoft.Json;
using NUnit.Framework;
using RichardSzalay.MockHttp;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Exsample.Test.Common.Util;
using Exsample.Net.Http;
using Moq;
using System.Web;
using System;

namespace Exsample.Module.Test
{
    [TestFixture]
    public class GetStreamAsync : ClientContext
    {
        private string url;
        private Stream stream;
        private Error error;

        /// <summary>
        /// 成功時(status=OK)のMockResponseMessageをセット
        /// </summary>
        private void setSuccessContent()
        {
            mockHttpMessage.Expect($"{baseUrl}*")
                .Respond(() =>
                {
                    // Status 200時に実行されるCallBack。
                    // Task<HttpResponseMessage>型を返すようにする。
                    return Task.Factory.StartNew<HttpResponseMessage>(() =>
                    {
                        var res = new HttpResponseMessage();
                        res.Content = new StreamContent(stream);
                        return res;
                    });
                });
        }

        /// <summary>
        /// 失敗時(status=NG)のMockResponseMessageをセット
        /// </summary>
        private void setFailContent()
        {
            error = new Error();
            // シリアライズして文字列として第二引数に渡してます。
            var content = JsonConvert.SerializeObject(error);
            mockHttpMessage.Expect($"{baseUrl}*")
                .Respond(HttpStatusCode.InternalServerError, "application/json", content);
        }

        /// <summary>
        /// MOCKのセットアップ。
        /// </summary>
        /// <param name="isSuccess">成功テストか否か</param>
        public void mockSetup(bool isSuccess)
        {
            if (isSuccess)
            {
                // テスト用のダミーStreamを作成
                stream = StreamCreator.Create();
                url = "unittest/getasync";
                setSuccessContent();
            }
            else
            {
                url = "unittest/notfound";
                setFailContent();
            }
        }

        /// <summary>
        /// Status=OKのテスト
        /// </summary>
        /// <returns></returns>
        [Test]
        public async Task Success()
        {
            mockSetup(true);
            var result = await client.GetStreamAsync(url, null);
            Assert.AreEqual(stream, result);
        }

        /// <summary>
        /// Status=NGのテスト
        /// </summary>
        /// <returns></returns>
        [Test]
        public void Fail()
        {
            mockSetup(false);
            var exception = Assert.ThrowsAsync<ResponsException>(() =>
            {
                return client.GetStreamAsync(url, null);
            });
            Assert.AreEqual(HttpStatusCode.InternalServerError, exception.StatusCode);
            // クラス同時の比較は出来ないのでJSON化して比較
            AssertObject(error, JsonConvert.DeserializeObject<Error>(exception.Message));
        }
    }

    /// <summary>
    /// パラメーターのインスタンス化や共通のMock化を行う
    /// </summary>
    public class ClientContext : TestBase
    {
        protected TargetClient client; // テスト対象のメソッド
        Mock<HttpContextBase> mockContext;
        protected MockHttpMessageHandler mockHttpMessage;
        protected Guid userId;

        // 適当なURL
        protected string baseUrl = "http://exsample.com:8080/api/";

        protected class Error
        {
            public string ErrorDiscription { get; set; }
            public string ErrorMessage { get; set; }
            public Error()
            {
                this.ErrorDiscription = "error_description";
                this.ErrorMessage = "server_error";
            }
        }

        [SetUp]
        public void Setup()
        {
            // MockのHttpContextBaseを作成
            mockContext = FakeHttpContextBase.Create(); // HttpContextBaseをMock化をする。

            // Sessionを上書きする
            var session = new Mock<HttpSessionStateBase>();
            userId = Guid.NewGuid();
            session.Setup(x => x["userId"]).Returns(userId.ToString());
            mockContext.Setup(x => x.Session).Returns(session.Object);

            // 今回紹介するmockHttpMessageHandlerのインスタンスを作成。
            mockHttpMessage = new MockHttpMessageHandler();
            var httpClient = new HttpClient(mockHttpMessage);

            client = new TargetClient(mockContext.Object);
            client.HttpClient = httpClient;
        }
    }
}

テスト対象メソッドはStatus=NGならばResponsExceptionをスロー。
メッセージ部分はJSON形式にて返却されます。
成功時は返却されたHttpResponseMessage.ContentをStreamにして返却しています。

APIのクライアント部分だし通信されると困るので、何かいいメソッドがないか見てたらいいのを見つけました。
今度もこれを使ってゴリゴリ書いていきたいと思います。