如何对缓存等实现细节进行单元测试

时间:2009-07-02 03:30:50

标签: c# tdd nunit rhino-mocks

所以我的课程方法如下:

public class SomeClass
{
    ...

    private SomeDependency m_dependency;

    public int DoStuff()
    {
        int result = 0;
        ...
        int someValue = m_dependency.GrabValue();
        ...
        return result;
    }  
}

而且我决定每次调用m_dependency.GrabValue()而不是每次调用[Test] public void CacheThatValue() { var depend = MockRepository.GeneraMock<SomeDependency>(); depend.Expect(d => d.GrabValue()).Repeat.Once().Return(1); var sut = new SomeCLass(depend); int result = sut.DoStuff(); result = sut.DoStuff(); depend.VerifyAllExpectations(); } ,我真的想将值缓存在内存中(即在这个类中),因为我们每次都会得到相同的值(依赖关系会消失并从几乎没有变化的表中获取一些数据。

我遇到了问题,但试图在单元测试中描述这种新行为。我尝试了以下(我正在使用NUnit和RhinoMocks):

{{1}}

然而,这不起作用;即使没有对功能进行任何更改,此测试也会通过。我做错了什么?

2 个答案:

答案 0 :(得分:5)

我认为缓存与Do(ing)Stuff正交。我会找到一种方法将缓存逻辑拉到方法之外,或者通过更改SomeDependency或以某种方式包装它(我现在对基于lambda表达式的缓存类有一个很酷的想法 - yum)。

这样你的DoStuff测试不需要改变,你只需要确保它们与新的包装器一起使用。然后,您可以独立地测试SomeDependency或其包装器的缓存功能。使用架构良好的代码放置缓存层应该相当容易,您的依赖性和实现都不应该知道差异。

单元测试不应该是测试实现,他们应该测试行为。同时,受试者应该有一套狭义的行为。

要回答您的问题,您使用的是动态模拟,默认行为是允许任何未配置的呼叫。其他调用只返回“0”。您需要设置一个期望,即不再对依赖项进行调用:

depend.Expect(d => d.GrabValue()).Repeat.Once().Return(1);
depend.Expect(d => d.GrabValue()).Repeat.Never();

您可能需要输入录制/重播模式才能使其正常工作。

答案 1 :(得分:5)

这似乎是“测试驱动设计”的情况。如果缓存是SubDependency的实现细节 - 因此无法直接测试 - 那么可能需要公开它的一些功能(特别是其缓存行为) - 并且因为在SubDependency中公开它并不自然,所以需要暴露在另一个类中(让我们称之为“缓存”)。当然,在Cache中,行为是契约性的 - 公开的,因此是可测试的。

所以测试 - 和气味 - 告诉我们我们需要一个新的课程。测试驱动设计。不是很棒吗?