在 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 即可,如下圖所示。

單元測試代碼說明
可以看到,要測試 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。
測試案例如下所示。

整個開發跟重構過程,請見影片。
結論
那一行 request.Properties[HttpConfigurationKey] = new HttpConfiguration(); 是關鍵,漏了,就會一直噴奇怪的 NullReferenceException 跟 HttpConfiguration 有問題的錯誤資訊。
blog 與課程更新內容,請前往新站位置:http://tdd.best/
