你如何模拟asp.net mvc中的缓存对象?

时间:2022-04-26 04:14:08

How would I mock the caching object on the ControllerContext object for my unit tests? I have tried creating a wrapper class like the following (since the cache object is a sealed class) with no luck.

我如何模拟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 个解决方案

#1


20  

EDIT: I found an easier way to do this, at least when you are testing with an empty cache. Use HttpRunTime.Cache as the return value for your expectation on the Cache property of the HttpContext. For more advanced scenarios, using a wrapper and mocking may still be a better way to handle it -- for example, if you need to test exceptions from the cache.

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

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

Original:

原版的:

The wrapper class is the way to go, but I think that you are applying it in the wrong place. I would give my controller a CacheWrapper property, then create a constructor that allows me to pass in a CacheWrapper instance to which this property can be set. By default the controller creates a CacheWrapper using HttpContext.Current.Cache. In your test code, construct a mock CacheWrapper to pass into the controller's constructor. This way you don't need to create a mock Cache object at all -- which is hard because it's a sealed class.

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

Alternatively, you could just instantiate an instance of the Cache class and return it, since there is a public constructor for it. Using the mock has the advantage that you can verify that the Cache is being used via expectations, however, so I'd probably go with the wrapper.

或者,您可以实例化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


11  

I would recommend using Microsoft's new MemoryCache.Default approach. You will need to use .NET Framework 4.0 or later and include a reference to System.Runtime.Caching.

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

See article here --> http://msdn.microsoft.com/en-us/library/dd997357(v=vs.100).aspx

请参阅此处的文章 - > http://msdn.microsoft.com/en-us/library/dd997357(v=vs.100).aspx

MemoryCache.Default works for both web and non-web applications. So the idea is you update your webapp to remove references to HttpContext.Current.Cache and replace them with references to MemoryCache.Default. Later, when you decide to Unit Test these same methods, the cache object is still available and won't be null. (Because it is not reliant on an HttpContext.)

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

This way you don't even necessarily need to mock the cache component.

这样您甚至不一定需要模拟缓存组件。

#3


5  

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

#4


0  

You can try to use Typemock Isolator, it fakes sealed classes out of the box, so you won't need these wrappers.

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

#1


20  

EDIT: I found an easier way to do this, at least when you are testing with an empty cache. Use HttpRunTime.Cache as the return value for your expectation on the Cache property of the HttpContext. For more advanced scenarios, using a wrapper and mocking may still be a better way to handle it -- for example, if you need to test exceptions from the cache.

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

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

Original:

原版的:

The wrapper class is the way to go, but I think that you are applying it in the wrong place. I would give my controller a CacheWrapper property, then create a constructor that allows me to pass in a CacheWrapper instance to which this property can be set. By default the controller creates a CacheWrapper using HttpContext.Current.Cache. In your test code, construct a mock CacheWrapper to pass into the controller's constructor. This way you don't need to create a mock Cache object at all -- which is hard because it's a sealed class.

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

Alternatively, you could just instantiate an instance of the Cache class and return it, since there is a public constructor for it. Using the mock has the advantage that you can verify that the Cache is being used via expectations, however, so I'd probably go with the wrapper.

或者,您可以实例化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


11  

I would recommend using Microsoft's new MemoryCache.Default approach. You will need to use .NET Framework 4.0 or later and include a reference to System.Runtime.Caching.

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

See article here --> http://msdn.microsoft.com/en-us/library/dd997357(v=vs.100).aspx

请参阅此处的文章 - > http://msdn.microsoft.com/en-us/library/dd997357(v=vs.100).aspx

MemoryCache.Default works for both web and non-web applications. So the idea is you update your webapp to remove references to HttpContext.Current.Cache and replace them with references to MemoryCache.Default. Later, when you decide to Unit Test these same methods, the cache object is still available and won't be null. (Because it is not reliant on an HttpContext.)

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

This way you don't even necessarily need to mock the cache component.

这样您甚至不一定需要模拟缓存组件。

#3


5  

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

#4


0  

You can try to use Typemock Isolator, it fakes sealed classes out of the box, so you won't need these wrappers.

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