验证单元测试中的交互是否破坏封装?

验证单元测试中的交互是否破坏封装?

问题描述:

正如我正在学习更多关于单元测试的知识,我已经认识到这个领域中众所周知的行为(交互)与状态验证方法。验证单元测试中的交互是否破坏封装?

验证某些系统的状态在执行某个操作后对我来说似乎是合乎逻辑的。

关于验证被测试类与其他某个组件的相互作用,可以这么说吗?我仍然不是100%信服。

例如:

public void DoSomething(IDependency dependency) 
{ 
    // some code ... 

    dependency.Method(); 

    dependency.Method2(); 

    // some more code ... 
} 

的,这对当前实现DoSomething的()的调用方法()和method2(),并是可测试使用嘲笑有任何真正的价值?

是不是调用这两个方法的DoSomething()的实现细节?

与基于状态的验证相比,似乎验证交互更加脆弱,并且还打破了封装(测试此方法隐藏的内容)。

+0

这取决于_what_您正在测试。如果你正在测试_interactions_,那就好了。如果你正在测试其他东西...可能不是。 – Oded 2013-03-13 21:11:15

+1

我想说,方法调用的排序只有在公共接口定义调用它们的设置顺序时才应该测试。即“在读之前打开”或类似的东西。在这种情况下,使用接口的模拟实现方法可能是个好主意,但归结为:“你在哪里绘制测试代码?” – 2013-03-13 21:16:30

恩,好吧,如果你想验证DoSomething(IDependency dependency)的行为,我会说你一定要验证你的依赖被调用。

这就是mock的典型用途。你想确保你的依赖在你的SuT中正确处理。你不想被你的依赖关系的实现细节困扰。

所以我最好的测试会是这样的:

[Test] 
public void DoSomething() 
{ 
    var sut = new Whatever(); 
    var dependencyMock = MockRepository.GenerateMock<IDependency>(); 
    dependencyMock.Stub(() => mock.Method1()).Repeat = 1; 
    dependencyMock.Stub(() => mock.Method2()).Repeat = 1; 
    sut.DoSomething(dependencyMock); 

    // verify that expected methods are being called 
    dependencyMock.VerifyAllExpectations(); 

    // make some meaningful assert for sut. 
    Assert. // ... etc 
} 

注意,我打这个工作室没有开放,所以所有的错别字模模错误:)。只是想给你一个想法。


如果你只是'粘合'功能在一起。就像一个类没有其他的东西,然后调用依赖对象的方法,你可能会值得调查是否应该重构'代码味道'。但在你上面提供的例子中,我会说你做得很好,你应该去做。

如果某个规范(某些人称之为测试)要求进行某种交互,则还应验证是否属于这种情况,并对此进行单元测试。例如,如果需要SUT保存文件,则应该确认已调用IFileSystem.Save(...)。如果要求SUT在文件存在的情况下应使用新文件名来保护它,则应验证IFileSystem.Save(...)已使用正确的文件名进行调用,并且已调用IFileSystem.Exists(...)。这是最好的互动测试。

使用FakeItEasy它会是这个样子:

// arrange 
var fileSystem = A.Fake<IFileSystem>(); 
A.CallTo(() => fileSystem.Exists("file.txt")).Returns(true); 
A.CallTo(() => fileSystem.Exists("file1.txt")).Returns(false); 
var sut = new SystemUnderTest(fileSystem); 

// act 
sut.DoSomething(); // do something that eventually saves file.txt 

// assert 
A.CallTo(() => fileSystem.Exists("file.txt")).MustHaveHappened(); 
A.CallTo(() => fileSystem.Exists("file1.txt")).MustHaveHappened(); 
A.CallTo(() => fileSystem.Save("file1.txt")).MustHaveHappened();