ASP.NET MVC 프로젝트를 이용하여 웹 개발을 하고 있으면 반드시 최소한 한 번은 사용하게 되는 HttpContext 인스턴스가 있다. 이 인스턴스는 보통 HttpContext.Current 형태의 싱글톤으로 호출하여 사용할 수 있으며 MVC 패턴에서는 Controller.HttpContext 속성이 이를 대신하고 있다. 하지만, 여기서 이 HttpContext 속성이 갖는 가장 큰 문제는 오로지 get만 지원하고 있어서, 임의의 HttpContext 값을 설정하는 의존성 주입을 할 수 없다는 것이다. 이를 위해 @Paul Hadfield는 자신의 블로그 포스트에서 이 HttpContext 속성을 재정의하는 방법을 제시한다. 예전 포스트에서 언급한 내용은 HttpContext 대신 HttpContextBase를 사용하면 유닛테스트에 유용하다 정도였지만, 여기서는 어떻게 유닛테스트에서 이것을 활용할 수 있을까에 대한 내용이 될 것이다.
생성자를 이용한 의존성 주입
public abstract class BaseController : Controller
{
public new HttpContextBase HttpContext { get; private set; }
protected BaseController()
{
this.HttpContext = base.HttpContext;
}
protected BaseController(HttpContextBase httpContext)
{
if(httpContext == null)
{
throw new ArgumentNullException("httpContext");
}
this.HttpContext = httpContext;
}
}
위와 같이 베이스 콘트롤러를 정의하고 그 안에서 HttpContext를 재정의한 후 생성자를 통해 의존성을 주입하는 방식을 쓰는 것이다. 이를 적용하면 아래와 같은 형태가 될 수 있다.
public class HomeController : BaseController
{
public HomeController(HttpContextBase httpContext)
: base(httpContext)
{
}
}
실제로 Unity 또는 Autofac 등과 같은 IoC 컨테이너들을 사용할 경우 콘트롤러는 더욱 많은 인스턴스들을 의존성 주입에 필요로 하게 되므로, 개인적으로는 이 HttpContext와 같은 것들은 Setter 메소드를 통해 의존성 주입을 하는 것을 권장한다. 하지만, 이것은 취향이니까 존중. 개인적으로는 아래와 같은 방법을 선호한다.
세터 메소드를 용한 의존성 주입
public abstract class BaseController : Controller
{
public new HttpContextBase HttpContext { get; private set; }
protected BaseController()
{
this.HttpContext = base.HttpContext;
}
public void SetHttpContext(HttpContextBase httpContext)
{
if (httpContext == null)
throw new ArgumentNullException("httpContext");
this.HttpContext = httpContext;
}
}
추가 의존성 주입
물론, 세션이나 쿠키 같은 내용들은 별도의 인스턴스를 통해 의존성을 주입해야 한다. 만약 세션 인스턴스도 의존성 주입이 필요하다면 아래와 같은 내용을 추가할 수 있다.
public abstract class BaseController : Controller
{
public new HttpContextBase HttpContext { get; private set; }
public new HttpSessionStateBase Session { get; private set; }
protected BaseController()
{
this.HttpContext = base.HttpContext;
}
public void SetHttpContext(HttpContextBase httpContext)
{
if (httpContext == null)
throw new ArgumentNullException("httpContext");
this.HttpContext = httpContext;
}
public void SetHttpSessionState(HttpSessionStateBase httpSessionState)
{
if (httpSessionState == null)
throw new ArgumentNullException("httpSessionState");
this.Session = httpSessionState;
}
}
이렇게 의존성 주입이 가능한 HTTP 추상 클라스들의 리스트는 아래와 같다.
HttpContextBaseHttpRequestBaseHttpResponseBaseHttpServerUtilityBaseHttpSessionStateBase
유닛테스트 예시
이렇게 콘트롤러를 만들어 놓으면 유닛테스트에서는 아래와 같은 형태로 사용이 가능하다. 여기서는 NUnit과 NSubstitute를 사용한다고 가정한다.
[Test]
public void Test()
{
var httpContext = Substitute.For();
var controller = new HomeController();
controller.SetHttpContext(httpContext);
// Testing logics here
}
물론, 필요에 따라 httpContext로 목킹한 인스턴스에 다양한 내용을 추가 가능하다.
맺으며
지금까지 간략하게 HttpContext 인스턴스를 ASP.NET MVC 콘트롤러에 주입하는 방법에 대해 논의해 보았다. 이 방법이 가장 옳다는 것은 아니고 이런 식으로 하면 되겠다 하는 것이다보니 언제든 이보다 더욱 효과적인 방법을 찾을 경우 이 포스트는 꾸준히 업데이트가 될 것이다.