【问题标题】:Mocking System.Web.Caching.Cache - Mock or check for null?模拟 System.Web.Caching.Cache - 模拟或检查空值?
【发布时间】:2012-04-30 10:59:30
【问题描述】:

我正在编写一些单元测试,我想知道模拟 Cache 是否有利,如果是,如何模拟?

目前在我的测试中,我正在模拟 HttpContextBase 并将其包装在自定义 HttpContextFactory 中:

var mockedHttpContextBase = new Mock<HttpContextBase>();

IHttpContextFactory httpContextFactory = new HttpContextFactory 
{ 
     Current = mockedHttpContextBase.Object 
};

当我的代码使用 IHttpContextFactory 时,我会在对其执行任何操作之前检查缓存是否为空。

var cache = _httpContextFactory.Current.Cache;

Func<SomeReturnType> doSomeWork = () => _foo.someMethodIExecute(param1,param2);

return cache != null ? cache.GetOrStore("doSomeWorkCacheKey",doSomeWork, 900) 
                     : doSomeWork.Invoke();

每次我使用缓存时都像这样检查缓存是否为空是否正确,或者您是否也会在测试中模拟缓存,以便在运行单元测试时它不为空?

【问题讨论】:

    标签: c# unit-testing caching design-patterns moq


    【解决方案1】:

    经过一番搜索,我似乎可以在编写单元测试时使用HttpRuntime.Cache 而不是System.Web.Caching.Cache

    这样:

    var mockedHttpContextBase = new Mock<HttpContextBase>();
    mockedHttpContextBase.Setup(m => m.Cache).Returns(HttpRuntime.Cache);
    

    缓存永远不应该是null(如果是,那将是一个适当的例外),因此我可以从我的代码中删除空引用检查。

    【讨论】:

    • 使用 HttpRuntime.Cache 避开 unMoq'able 密封 System.Web.Caching.Cache 的好举措
    • 这对我不起作用,因为我的测试是异步的。因此,HttpRuntime.Cache 共享状态,而不是每次测试。它在单独运行测试时确实有效。
    • 要清楚 - 在这种情况下,您不会实例化缓存。在使用模拟版本之前,只需将您的项目添加到 HttpRuntime.Cache 即可。 HttpRuntime.Cache.Add(cacheKey, sampleUser, null, DateTime.Now.AddHours(2), Cache.NoSlidingExpiration, CacheItemPriority.Normal, null); var context = new Mock&lt;HttpContextBase&gt;(); context.Setup(x =&gt; x.Cache).Returns(HttpRuntime.Cache);
    • 这对我也不起作用,因为我的测试是并行运行的。 @BartVerkoeijen,你有没有找到一种方法来模拟每个测试的缓存对象?
    • @ShervinShahrdar 我没有找到方法,但作为一种解决方法,我在 Visual Studio 的测试资源管理器中选择了一个切换开关以同步运行测试,如果我没记错的话。
    【解决方案2】:

    如果您的代码假定缓存可以是null 并在访问它之前执行检查(就像现在一样),您需要对每个缓存访问进行两次单元测试:

    • 缓存存在并存储和检索项目(检查GetOrStore调用)
    • 缓存为空,您只需断言委托调用

    如果这是常见模式(空值检查),我建议不要在每次需要缓存依赖项时进行两次测试,而是将其包装到 Null Object Pattern 并对其进行一次测试,然后只需使用 NOP 作为可以模拟的依赖项.

    编辑:缓存“模拟”示例

    var cache = new Cache();
    // Add takes more parameters; fill whatever is necessary to make it work
    cache.Add("doSomeWorkCacheKey", doSomeWork, ...);
    var mockedHttpContextBase = new Mock<HttpContextBase>();
    // tell your mock to return pre-configured cache
    mockedHttpContextBase.Setup(m => m.Cache).Returns(cache);
    
    IHttpContextFactory httpContextFactory = new HttpContextFactory 
    { 
        Current = mockedHttpContextBase.Object 
    };
    

    【讨论】:

    • 谢谢吉米。关于第 1 点,您将如何编写依赖于现有缓存的测试?在这种情况下,您将如何模拟缓存来执行测试?
    • @JamieDixon:你不能真正模拟Cache,因为它是密封的;您必须使用真正的 Cache 实现对其进行预配置,并使您的 HttpContextBase 模拟在请求时返回该预配置缓存。我在帖子中添加了示例。
    • 再次感谢吉米。感谢您的时间和帮助。在进行更多搜索后,我自己添加了一个答案。我似乎使用HttpRuntime.Cache 更合适,因为它会实例化它需要工作的所有东西,因为如果没有配置所有东西,System.Web.Caching.Cache 将抛出NullReferenceExeption
    猜你喜欢
    • 1970-01-01
    • 2021-10-22
    • 1970-01-01
    • 2022-01-09
    • 2011-04-27
    • 1970-01-01
    • 1970-01-01
    • 2014-01-17
    • 1970-01-01
    相关资源
    最近更新 更多