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

时间:2013-03-13 21:08:54

标签: c# .net unit-testing

随着我对单元测试的了解越来越多,我逐渐意识到这一领域众所周知的行为(交互)与状态验证方法。

执行某项操作后验证某些系统的状态对我来说似乎是合理的。

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

例如:

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

    dependency.Method();

    dependency.Method2();

    // some more code ...
}

DoSomething()的当前实现调用Method()和Method2()并且使用模拟是否可测试这一事实是否具有任何实际价值?

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

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

2 个答案:

答案 0 :(得分:3)

如果规范(有人称之为测试)要求进行某种交互,您还应该验证是否属于这种情况并对此进行单元测试。例如,如果要求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();

答案 1 :(得分:0)

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

这就是模拟通常的目的。您希望确保在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
}

请注意,我在没有打开工作室的情况下键入了这个,所以所有模数错别字模块错误:)。只是为了给你一个想法。


如果您只是将“功能”粘合在一起。就像一个类什么都不做,然后在依赖对象上调用方法,你可能值得研究是否应该重构“代码味道”。但是在上面提到的例子中,我说你做得很好,你应该去做。