c# - 如何对使用缓存的服务进行单元测试?

标签 c# asp.net-mvc unit-testing caching

我有一个服务层,它有一系列的方法。这些方法都实现了缓存,如下所示:

string key = "GetCategories";
if (CacheHandler.IsCachingEnabled() && !CacheHandler.ContainsKey(key))
{
    var categories = RequestHelper.MakeRequest("get_category_index")["categories"];
    var converted = categories.ToObject<List<Category>>();
    CacheHandler.InsertToCache(key,converted);
    return converted;
}
return CacheHandler.GetCache(key) as List<Category>;

现在,问题是我还想进行单元测试,如下所示:

[TestMethod]
public void GetCategories()
{
    IContentService contentService = new ContentService();
    var resp = contentService.GetCategories();
    Assert.IsNotNull(resp,"Should not be null");
}

问题是,我的 CacheHandler 中的 HttpContext.Current 在单元测试期间为 null(很明显)。

解决此问题的最简单方法是什么?

(请尽可能具体,因为我之前没有做过很多单元测试)

最佳答案

这尖叫 dependency injection 。我看到的主要问题是您访问 CacheHandler静态地,所以在单元测试中,你:
a) 不“测试”CacheHandler 就无法测试服务还有
b) 无法提供任何其他 CacheHandler到服务,例如 mocked 一个

如果您的情况可行,我会重构或至少包装 CacheHandler以便服务访问它的一个实例。在单元测试中,您可以为服务提供“假”CacheHandler。 ,这不会访问 HttpContext,也可以让您对测试本身有很好的控制(例如,您可以测试当一个项目被缓存时和当它不在两个绝对独立的单元测试中时会发生什么)

对于模拟部分,我想最简单的方法是创建一个接口(interface),然后使用一些专为测试设计的自动模拟/代理生成框架,例如 Rhino Mocks (但还有更多,恰好我正在使用这个并且非常满意:))。另一种方法(对初学者来说更容易,但在实际开发中更麻烦)是简单地设计 CacheHandler (或其包装器)以便您可以继承它并自己覆盖行为。

最后,对于注入(inject)本身,我找到了一个方便的“模式”,它利用了 C# 默认方法参数和标准构造函数注入(inject)。服务构造函数如下所示:

public ContentService(ICacheHandler cacheHandler = null)
{
    // Suppose I have a field of type ICacheHandler to store the handler
    _cacheHandler = cacheHandler ?? new CacheHandler(...);
}

所以在应用程序本身中,我可以调用不带参数的构造函数(或者让框架构造服务,如果它是 ASP.NET 处理程序、WCF 服务或其他类型的类)并且在单元测试中,我可以提供任何内容实现上述接口(interface)。

如果是 Rhino Mocks,它可能看起来像这样:

var mockCacheHandler = MockRepository.GenerateMock<ICacheHandler>();
// Here I can mock/stub methods and properties, set expectations etc...
var sut = new ContentService(mockCacheHandler);

关于c# - 如何对使用缓存的服务进行单元测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18043053/

相关文章:

c# - 简单注入(inject)器 : batch registration of classes with multiple interfaces

c# - 如何获取 SharePoint 2010 Web 应用程序范围的功能以仅在一个 Web 应用程序上激活

c# - 线程在什么情况下可以同时进入一个锁(Monitor)区域不止一次?

c# - wpf:嵌套菜单项的绑定(bind)

asp.net-mvc - 使用多上下文应用程序自定义 ASPNET Identity 一对多关系

javascript - jasmine getFixtures 不是函数

jquery - @Scripts.Render 和 @Styles.Render 的放置位置

asp.net-mvc - asp.net 身份中的一次性登录链接

python - 如何对使用 python-multiprocessing 的代码进行单元测试

c# - 在 NUnit 测试中创建 App Domain 时出现异常