【问题标题】:How do you mock the caching object in asp.net mvc?你如何模拟 asp.net mvc 中的缓存对象?
【发布时间】:2009-04-23 19:39:34
【问题描述】:

如何模拟 ControllerContext 对象上的缓存对象以进行单元测试?我尝试过创建一个如下所示的包装类(因为缓存对象是一个密封类),但没有成功。

var mockControllerContext = new Mock<ControllerContext>(); 
var mockhttpContext = new Mock<HttpContextBase>();            

mockhttpContext.SetupGet(o => o.Cache).Returns(
         new CacheWrapper(mockControllerContext.Object.HttpContext.Cache));

mockControllerContext.SetupGet(
                          o => o.HttpContext).Returns(mockhttpContext.Object);
this.tennisMatchupController.ControllerContext = mockControllerContext.Object; 

【问题讨论】:

    标签: asp.net-mvc unit-testing mocking


    【解决方案1】:

    编辑:我找到了一种更简单的方法,至少在您使用空缓存进行测试时是这样。使用 HttpRunTime.Cache 作为您对 HttpContext 的 Cache 属性的期望的返回值。对于更高级的场景,使用包装器和模拟可能仍然是更好的处理方式——例如,如果您需要测试缓存中的异常。

    var httpContext = MockRepository.GenerateMock<HttpContextBase>();
    httpContext.Expect( h => h.Cache ).Return( HttpRunTime.Cache ).Repeat.Any()
    

    原创

    包装类是要走的路,但我认为你在错误的地方应用它。我会给我的控制器一个 CacheWrapper 属性,然后创建一个构造函数,允许我传入一个可以设置该属性的 CacheWrapper 实例。默认情况下,控制器使用 HttpContext.Current.Cache 创建一个 CacheWrapper。在您的测试代码中,构造一个模拟 CacheWrapper 以传递给控制器​​的构造函数。这样一来,您根本不需要创建模拟 Cache 对象——这很困难,因为它是一个密封的类。

    或者,您可以只实例化 Cache 类的实例并返回它,因为它有一个公共构造函数。使用模拟的优点是您可以通过期望验证缓存是否正在使用,所以我可能会使用包装器。

    public class CacheWrapper
    {
      private Cache Cache { get; set; }
    
      public CacheWrapper()
      {
         this.Cache = HttpContext.Current.Cache;
      }
    
      public virtual Object Add( string key,
                                 Object value,
                                 CacheDependency dependencies,
                                 DateTime absoluteExpiration,
                                 TimeSpan slidingExpiration,
                                 CacheItemPriority priority,
                                 CacheItemRemovedCallback onRemoveCallback )
      {
         this.Cache.Add( key,
                         value,
                         dependencies,
                         absoluteExpiration,
                         slidingExpiration,
                         priority,
                         onRemoveCallback );
      }
    
      ...wrap other methods...
    }
    
    
    public class BaseController : Controller
    {
        private CacheWrapper { get; set; }
    
        public BaseController() : this(null) { }
    
        public BaseController( CacheWrapper cache )
        {
            this.CacheWrapper = cache ?? new CacheWrapper();
        }
    }
    
    [TestMethod]
    public void CacheTest()
    {
       var wrapper = MockRepository.GenerateMock<CacheWrapper>();
    
       wrapper.Expect( o => o.Add( ... ) ).Return( ... );
    
       var controller = new BaseController( wrapper );
    
       var result = controller.MyAction() as ViewResult;
    
       Assert.AreEqual( ... );
    
       wrapper.VerifyAllExpectations();
    }
    

    【讨论】:

      【解决方案2】:

      我建议使用 Microsoft 的新 MemoryCache.Default 方法。您将需要使用 .NET Framework 4.0 或更高版本并包含对 System.Runtime.Caching 的引用。

      请看这里的文章 --> http://msdn.microsoft.com/en-us/library/dd997357(v=vs.100).aspx

      MemoryCache.Default 适用于 Web 和非 Web 应用程序。所以想法是你更新你的 webapp 以删除对 HttpContext.Current.Cache 的引用并将它们替换为对 MemoryCache.Default 的引用。稍后,当您决定对这些相同的方法进行单元测试时,缓存对象仍然可用并且不会为空。 (因为它不依赖于 HttpContext。)

      这样你甚至不需要模拟缓存组件。

      【讨论】:

      • 很好的注释确实有助于简化整个过程
      【解决方案3】:
      HttpContext.Current = new HttpContext(new HttpRequest(null, "http://tempuri.org", null), new HttpResponse(null));
      

      【讨论】:

        【解决方案4】:

        您可以尝试使用Typemock Isolator,它会伪造开箱即用的密封类,因此您不需要这些包装器。

        【讨论】:

          猜你喜欢
          • 2015-09-01
          • 2017-03-12
          • 2017-08-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-05-01
          • 1970-01-01
          相关资源
          最近更新 更多