如何在ASP.NET MVC中模拟缓存对象?

17

我该如何在单元测试中模拟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; 
4个回答

21

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

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

Translated:

包装类是解决问题的方式,但我认为你把它应用在了错误的地方。我会给我的控制器一个CacheWrapper属性,并创建一个构造函数,允许我传递一个CacheWrapper实例,并将其设置为该属性。默认情况下,控制器使用HttpContext.Current.Cache创建CacheWrapper。在测试代码中,构造一个模拟CacheWrapper并将其传递到控制器的构造函数中。这样你就不需要创建一个模拟的Cache对象——这很难,因为它是一个密封类。

另外,你也可以只实例化一个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();
}

12
我建议使用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应用程序。因此,您需要更新您的Web应用程序,删除对HttpContext.Current.Cache的引用,并将其替换为对MemoryCache.Default的引用。稍后,当您决定对这些相同的方法进行单元测试时,缓存对象仍然可用且不会为空。(因为它不依赖于HttpContext)
这样,您甚至不一定需要模拟缓存组件。

1
很棒的笔记,确实帮助了我,使整个过程更简单。 - P6345uk

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

0

你可以尝试使用Typemock Isolator,它可以直接模拟密封类,因此你不需要这些包装器。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接