嘲笑SUT本身

时间:2011-04-20 05:50:23

标签: unit-testing mocking

有时候我觉得嘲笑测试对象本身的欲望,我知道它看起来很奇怪。

这是一个例子。我使用C#,但没关系。想象一下,你有一个班级来管理一些收藏。它有方法

public void Insert(int position, T element)

public void Delete(int position)

这些方法的逻辑并不是很简单。除了修改集合之外,他们还可以举例说明事件,订阅/取消订阅事件,获取/发布资源或执行计算。检查此功能的单元测试涵盖了这些方法。

现在添加一个方法

public void Replace(int position, T element)
{
    Delete(position);
    Insert(position, element);
}

我不完全确定,但对我而言,这种方法再次测试所有这些东西并不值得。相反,检查这个方法是否真的首先调用这两个方法是有意义的。

如果前两个方法属于另一个类或接口,则使用现代模拟框架很容易。但这些方法属于SUT。

有可能做我想要的吗?如果没有,那么为什么呢?这是嘲弄方法的意外缺陷还是设计上的缺陷。如果是第一个,是否有解决方法?如果是第二个,我应该如何设计和测试所描述的程序?

2 个答案:

答案 0 :(得分:3)

我不确定您使用的是什么框架,但我能够在Rhino.Mocks中执行此操作。我没有实例化我的SUT,而是创建了它的存根(请注意,我不是创建接口的存根,而是创建对象本身的存根)。存根仍将运行SUT的代码,但也会跟踪我的呼叫。

//Create a stub of my SUT, passing the constructor arguments to GenerateStub()
_sut = MockRepository.GenerateStub<SutClass>(_mockedDependency);

这允许我断言像......

_sut.AssertWasCalled(x => x.DoTheStuff(_argObject1, _argObject2),
                     o => o.Repeat.Once());

非常漂亮。现在我只需要确保我不滥用它;)

修改 我应该添加它来测试方法DoTheStuff是否被调用,看起来它可能需要overridable(C#中的virtual)。我认为没有必要,但我前几天写的测试表明相反。如果我错了,请告诉我。

答案 1 :(得分:1)

首先,是的,至少在FakeItEasy中可以做到。

其次,这可能不是一个好主意。我说可能是因为有些情况下至少不是一个非常糟糕的主意。在上面的例子中,我会说这可能是一个坏主意,但我不确定这是一个真实世界的例子。这是一个坏主意,因为替换方法也可以作为您的类的扩展方法,这样就很容易测试。

但是,如果您认为在您的情况下这是一个好主意,这就是您在FakeItEasy中的做法(确保您要断言的方法是虚拟的):

// In your set up
var sut = A.Fake<MySut>();
A.CallTo(sut).CallsBaseMethod();

// In your test of the Replace-method
A.CallTo(() => sut.Delete(thePosition)).MustHaveHappened();

我不确定在其他模拟框架中执行此操作是否非常容易,但这可能会指向正确的方向。

正如我所说,在大多数情况下这可能不是一个好主意,但我自己也使用过这种技术,它可能很有用。