随着我对单元测试的了解越来越多,我逐渐意识到这一领域众所周知的行为(交互)与状态验证方法。
执行某项操作后验证某些系统的状态对我来说似乎是合理的。
关于验证被测试类与其他组件的交互,可以这样说吗?我仍然不是100%相信。
例如:
public void DoSomething(IDependency dependency)
{
// some code ...
dependency.Method();
dependency.Method2();
// some more code ...
}
DoSomething()的当前实现调用Method()和Method2()并且使用模拟是否可测试这一事实是否具有任何实际价值?
这两个方法的调用不是DoSomething()的实现细节吗?
与基于状态的验证相比,验证交互似乎更加脆弱,并且还打破了封装(测试此方法隐藏的内容)。
答案 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
}
请注意,我在没有打开工作室的情况下键入了这个,所以所有模数错别字模块错误:)。只是为了给你一个想法。
如果您只是将“功能”粘合在一起。就像一个类什么都不做,然后在依赖对象上调用方法,你可能值得研究是否应该重构“代码味道”。但是在上面提到的例子中,我说你做得很好,你应该去做。