Write a Unit Test for ExceptionFilterAttribute

在 ASP.NET MVC 與 ASP.NET Web API 中,會使用有彈性且關注點分離的 ActionFilter 來進行進出 Action 的控制。

然而 ActionFilter 大部分都是在處理 HttpRequest 與 HttpResponse 的內容,該怎麼為 ActionFilter 撰寫獨立的單元測試呢?這篇文章將以一個簡單的自訂 ExceptionFilter 來當範例,說明怎麼模擬 HttpRequest HttpResponse,尤其是使用了 HttpRequestMessage CreateResponse() 系列方法。

ExceptionFilter 產品代碼說明

當這個 ActionFilter 所覆蓋的範圍內發生 exception 時,為避免吐回給呼叫端的 status code 是 500 Internal Error,而是希望改成 HttpStatus 是 200 OK 搭配自訂狀態碼。ActionFilter 代碼如下所示。

Controller 在使用時,只需要在 Action 或 Controller 上標記 Attribute 即可,如下圖所示。

這樣回傳 200 OK 搭配自訂狀態碼的作法,雖然非主流,卻在一些與舊系統相容的需求上很常見。有興趣的朋友可以參考黑暗執行緒的這篇文章【閒聊 - Web API 是否一定要 RESTful?

單元測試代碼說明

可以看到,要測試 ApiExceptionFilterAttribute 的 OnException(),最麻煩的地方其實是在初始化 HttpActionExecutedContext,這個 context 包含了 HttpRequest HttpResponse 的資訊,因為是 ExceptionFilter 的事件,所以還包含了 Exception 的 property。

產生HttpActionExecutedContext的代碼如下所示:

        private HttpActionExecutedContext CreateExecutedContext(Exception exception)
        {
            return new HttpActionExecutedContext
            {
                ActionContext = new HttpActionContext
                {
                    ControllerContext = new HttpControllerContext
                    {
                        Request = GetHttpRequestMessage()
                    }
                },
                Exception = exception,
                Response = new HttpResponseMessage()
            };
        }

        private HttpRequestMessage GetHttpRequestMessage()
        {
            var request = new HttpRequestMessage();
            request.Properties[HttpConfigurationKey] = new HttpConfiguration();

            return request;
        }
代碼中的HttpConfigurationKey 是 import static: using static System.Web.Http.Hosting.HttpPropertyKeys;

由於在 Web API 的 ActionFilter 中,我們很常會使用 HttpRequestMessage.CreateResponse() 來產生同樣 content-type 的 response,而不是自己 new HttpResponseMessage() 來定義回傳內容,因此需要使用到 HttpConfiguration

測試案例如下所示。

整個開發跟重構過程,請見影片。

範例代碼我也一併放上 gist 上了,請自行參考。

結論

那一行 request.Properties[HttpConfigurationKey] = new HttpConfiguration(); 是關鍵,漏了,就會一直噴奇怪的 NullReferenceException HttpConfiguration 有問題的錯誤資訊。

想知道更多的單元測試技巧?請參考:單元測試實戰操練營 201905 第六梯次﹣台北,目前還有一張早鳥票

blog 與課程更新內容,請前往新站位置:http://tdd.best/