2009-01-28 4 views
33

HttpContext.Current.Cache class을 사용하는 클래스에 대한 단위 테스트를 만들 때 NUnit을 사용할 때 오류가 발생합니다. 기능은 기본입니다 - 항목이 캐시에 있는지 확인하고하지 않을 경우, 그것을 만들어에 넣어 :단위 테스트 HttpContext.Current.Cache 또는 C#의 다른 서버 측 메서드?

if (HttpContext.Current.Cache["Some_Key"] == null) { 
    myObject = new Object(); 
    HttpContext.Current.Cache.Insert("Some_Key", myObject); 
} 
else { 
    myObject = HttpContext.Current.Cache.Get("Some_Key"); 
} 

단위 테스트에서이 호출, 그것은 NullReferenceException에서 처음 Cache가 발생할 때 실패 선. Java에서는 서버 측 코드를 테스트 할 때 Cactus을 사용합니다. C# 코드에서 사용할 수있는 비슷한 도구가 있습니까? This SO question에 모의 프레임 워크가 언급되었습니다.이 방법을 테스트 할 수있는 유일한 방법입니까? C# 용 테스트를 실행하는 비슷한 도구가 있습니까?

또한 단위 테스트를 위해 특별히 코드를 작성하고 서버에서 실행할 때 항상 유효하다고 가정하기 때문에 Cache이 null인지 확인하지 않습니다. 이것이 유효합니까? 아니면 캐시 주위에 null 체크를 추가해야합니까?

답변

42

이 작업을 수행하는 방법은 HttpContext 또는 다른 유사한 클래스를 직접 사용하지 않고 mock으로 대체하는 것입니다. 결국 HttpContext가 제대로 작동하는지 (즉, Microsoft의 작업인지) 테스트하지 않고, 필요할 때 호출 된 메서드를 테스트하려고합니다.

단계 (경우에 당신은 그냥 블로그의 부하를 파기하지 않고 기술을 알고 싶어) :

  1. 당신이 당신의 캐싱에 사용할 방법을 (를 GetItem 같은 아마 일을 기술하는 인터페이스를 작성, SetItem, ExpireItem). 이 ICACHE 전화하거나

  2. 을 원하는대로 그 인터페이스를 구현하는 클래스를 생성하고 실제 HttpContext를

  3. 동일한 인터페이스를 구현하는 클래스를 작성하는 방법을 통해 방법을 전달, 그냥 모의 캐시와 같은 역할을합니다. 객체를 저장하는 데 신경 쓰면 사전이나 무언가를 사용할 수 있습니다.

  4. 원본 코드를 변경하여 HttpContext를 전혀 사용하지 않고 대신 ICache 만 사용합니다.그런 다음 코드는 ICache의 인스턴스를 가져와야합니다. 클래스 생성자에서 인스턴스를 전달하거나 (이는 모든 종속성 삽입이 실제로 이루어집니다) 또는 일부 전역 변수에 고정시킬 수 있습니다.

  5. 프로덕션 응용 프로그램에서 ICache를 실제 HttpContext-Backed-Cache로 설정하고 단위 테스트에서 ICache를 모의 캐시로 설정하십시오.

  6. 이익!

2

일반적으로 단위 테스트 내에서 관련된 HttpContext를 구동하는 것은 완전히 악몽이므로 가능하면 피해야합니다.

나는 조롱에 관한 올바른 길을 가고 있다고 생각합니다. RhinoMocks (http://ayende.com/projects/rhino-mocks.aspx)를 좋아합니다.

아직 시도하지는 않았지만 MoQ에 대한 좋은 점을 읽었습니다 (http://code.google.com/p/moq).

당신이 정말로 C#으로 단위 테스트 할 웹 UI를 작성하려면 방법 사람들이 향하고있는 것으로 보인다는 .NET을 사용하는 경우

6

... 웹폼보다는 MVC 프레임 워크 (http://www.asp.net/mvc)를 사용하는 것입니다 3.5에서는 응용 프로그램에서 System.Web.Abstractions를 사용할 수 있습니다.

Justin Etheredge는 (캐시 클래스가 포함 된) HttpContext를 조롱하는 방법에 대해 post을 훌륭하게 사용합니다.

Justin의 예에서 HttpContextFactory.GetHttpContext를 사용하여 HttpContextBase를 내 컨트롤러에 전달합니다. 그들을 조롱 할 때, 나는 캐시 객체를 호출하기 위해 모의 (Mock)을 빌드한다.

+6

당신은 조롱 할 수 있습니다 HttpContextBase가있는 거의 모든 것이 있지만 Cache 속성은 그 중 하나가 아닙니다. HttpContextBase.Cache는 봉인 된 단위 테스트에서 사용할 수없고 조롱 할 수없는 System.Web.Caching.Cache 유형입니다 ... – chris166

0

이러한 프로그래밍 관련 질문은 인터페이스를 두 번 구현하는 인터페이스 기반 프로그래밍 모델을 요구합니다. 하나는 실제 코드 용이고 다른 하나는 모형 용입니다.

다음 인스턴스는 다음 호입니다. 이를 위해 몇 가지 디자인 패턴이 있습니다. 예를 들어 유명한 GangOfFour Creational 패턴 (GOF) 또는 Dependency Injection 패턴을 참조하십시오.

ASP.Net MVC는 실제로이 인터페이스 기반 방식을 사용하므로 단위 테스트에 훨씬 적합합니다.

28

나는 인터페이스를 사용하는 것이 최선의 선택 일 것이라고 동의하지만 때로는 기존 시스템을 바꿀 수없는 경우도 있습니다. 여기 내 프로젝트 중 하나에서 방금 만든 코드를 사용하면 원하는 결과를 얻을 수 있습니다. 예쁘거나 훌륭한 솔루션에서 가장 멀리 떨어져 있지만, 코드를 변경할 수 없다면 일을 끝내야합니다.

using System; 
using System.IO; 
using System.Reflection; 
using System.Text; 
using System.Threading; 
using System.Web; 
using NUnit.Framework; 
using NUnit.Framework.SyntaxHelpers; 

[TestFixture] 
public class HttpContextCreation 
{ 
    [Test] 
    public void TestCache() 
    { 
     var context = CreateHttpContext("index.aspx", "http://tempuri.org/index.aspx", null); 
     var result = RunInstanceMethod(Thread.CurrentThread, "GetIllogicalCallContext", new object[] { }); 
     SetPrivateInstanceFieldValue(result, "m_HostContext", context); 

     Assert.That(HttpContext.Current.Cache["val"], Is.Null); 

     HttpContext.Current.Cache["val"] = "testValue"; 
     Assert.That(HttpContext.Current.Cache["val"], Is.EqualTo("testValue")); 
    } 

    private static HttpContext CreateHttpContext(string fileName, string url, string queryString) 
    { 
     var sb = new StringBuilder(); 
     var sw = new StringWriter(sb); 
     var hres = new HttpResponse(sw); 
     var hreq = new HttpRequest(fileName, url, queryString); 
     var httpc = new HttpContext(hreq, hres); 
     return httpc; 
    } 

    private static object RunInstanceMethod(object source, string method, object[] objParams) 
    { 
     var flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; 
     var type = source.GetType(); 
     var m = type.GetMethod(method, flags); 
     if (m == null) 
     { 
      throw new ArgumentException(string.Format("There is no method '{0}' for type '{1}'.", method, type)); 
     } 

     var objRet = m.Invoke(source, objParams); 
     return objRet; 
    } 

    public static void SetPrivateInstanceFieldValue(object source, string memberName, object value) 
    { 
     var field = source.GetType().GetField(memberName, BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance); 
     if (field == null) 
     { 
      throw new ArgumentException(string.Format("Could not find the private instance field '{0}'", memberName)); 
     } 

     field.SetValue(source, value); 
    } 
} 
0

가있는 HttpContext에 문제가있다, 여기 말했다, 현재 Typemock 유일한 프레임 워크입니다 수 가짜 직접 래퍼 또는 추상화없이.

1

아마도 당신의 거리를 ... Phil Haack은 Rhino mock의 도움을 받아 asp mvc에서 httpcontext를 모의하는 방법을 보여 주지만 webforms에 적용될 수 있다고 상상합니다. 이 도움이

Clicky!!

희망.

0

캐시 개체는 .NET Framework의 봉인 된 영역이기 때문에 모의하기가 어렵습니다. 필자는 일반적으로 캐시 관리자 객체를 허용하는 캐시 래퍼 클래스를 작성하여이 문제를 해결합니다. 테스트를 위해 모의 캐시 관리자를 사용합니다. 생산을 위해 실제로 HttpRuntime.Cache에 액세스하는 캐시 관리자를 사용합니다.

기본적으로 캐시를 직접 추상화합니다.

16
HttpContext.Current = new HttpContext(new HttpRequest(null, "http://tempuri.org", null), new HttpResponse(null)); 
0

MVC 3 MOQ하여 이들의 예 :

내 컨트롤러 방법은 다음 행 가지고 이와 같이

model.Initialize(HttpContext.Cache[Constants.C_CustomerTitleList] 
as Dictionary<int, string>); 

을 임의 단위 테스트가 실패를 I는 설정하고 있지 않다로서 up HttpContext.Cache.내 단위 테스트에서

, 다음과 같이 내가 준비 :

HttpRuntime.Cache[Constants.C_CustomerTitleList] = new Dictionary<int, string>(); 

var mockRequest = new Mock<HttpRequestBase>(); 
mockRequest.SetupGet(m => m.Url).Returns(new Uri("http://localhost")); 

var context = new Mock<HttpContextBase>(MockBehavior.Strict); 
context.SetupGet(x => x.Request).Returns(mockRequest.Object); 
context.SetupGet(x => x.Cache).Returns(HttpRuntime.Cache); 

var controllerContext = new Mock<ControllerContext>(); 
controllerContext.SetupGet(x => x.HttpContext).Returns(context.Object); 

customerController.ControllerContext = controllerContext.Object; 
5

단위 테스트에서 캐시 구체적으로 다룰 수 있도록 새로운 접근 방식이있다.

Microsoft의 새로운 MemoryCache.Default 접근 방식을 사용하는 것이 좋습니다. .NET Framework 4.0 이상을 사용하고 System.Runtime.Caching에 대한 참조를 포함해야합니다. 여기

참조 기사 ->http://msdn.microsoft.com/en-us/library/dd997357(v=vs.100).aspx 모두 웹 및 비 웹 응용 프로그램에 적용됩니다 MemoryCache.Default

. 그래서 아이디어는 HttpContext.Current.Cache에 대한 참조를 제거하고 MemoryCache.Default에 대한 참조로 바꿀 webapp을 업데이트하는 것입니다. 나중에 이러한 동일한 메서드를 Unit Test로 실행하면 캐시 개체를 계속 사용할 수 있으므로 null이되지 않습니다. (HttpContext에 의존하지 않기 때문에)

이렇게하면 캐시 구성 요소를 조롱 할 필요가 없습니다. 당신이 테스트를 캐시에 대해주의를 해달라고하면

1

아래 수행 할 수 있습니다

[TestInitialize] 
    public void TestInit() 
    { 
     HttpContext.Current = new HttpContext(new HttpRequest(null, "http://tempuri.org", null), new HttpResponse(null)); 
    } 

은 또한 당신이

var controllerContext = new Mock<ControllerContext>(); 
     controllerContext.SetupGet(p => p.HttpContext.Session["User"]).Returns(TestGetUser); 
     controllerContext.SetupGet(p => p.HttpContext.Request.Url).Returns(new Uri("http://web1.ml.loc")); 
0

시도 할 수 아래처럼 MOQ 수 있습니다 ...

Isolate.WhenCalled(() => HttpContext.Current).ReturnRecursiveFake(); 
var fakeSession = HttpContext.Current.Session; 
Isolate.WhenCalled(() => fakeSession.SessionID).WillReturn("1"); 
관련 문제