我想编写一个单元测试,检查流已经复制到磁盘上了:
问题是CopyTo方法不是虚拟的所以我不能使用
inputMemoryStreamMock.Verify(c => c.CopyTo(outputMemoryStreamMock.Object));
我不知道如何模拟流: - /
这是我的测试方法:
[TestMethod]
public void Save_Stream_On_DestinationPath()
{
// Arrange
string fileName = "filename.pdf";
DateTime date = new DateTime(2013, 9, 27);
var serverConfigMock = new Mock<IServerConfigurationManager>();
serverConfigMock.Setup(config => config.ReportingBasePath).Returns(@"c:\reportsFolder");
var factoryReportFileResultMock = new Mock<IReportFileResultFactory>();
var timeManagementMock = new Mock<ITimeManagement>();
timeManagementMock.Setup(c => c.GetServerDate()).Returns(date);
var fileSystemMock = new Mock<IFileSystem>();
var fileInfoFactory = new Mock<IFileInfoFactory>();
var directoryInfoBaseMock = new Mock<DirectoryInfoBase>();
var inputMemoryStreamMock = new Mock<Stream>();
var outputMemoryStreamMock = new Mock<Stream>();
var reportFileHelper = new ReportFileHelper(serverConfigMock.Object, factoryReportFileResultMock.Object, fileSystemMock.Object);
inputMemoryStreamMock.Setup(c => c.CanRead).Returns(true);
outputMemoryStreamMock.Setup(c => c.CanWrite).Returns(true);
outputMemoryStreamMock.Setup(c => c.CanWrite).Returns(true);
fileSystemMock.Setup(c => c.FileInfo).Returns(fileInfoFactory.Object);
fileSystemMock.Setup(c => c.File.Create(It.IsAny<string>())).Returns(outputMemoryStreamMock.Object);
fileSystemMock.Setup(c => c.Directory.CreateDirectory(It.IsAny<string>())).Returns(directoryInfoBaseMock.Object);
// Act
reportFileHelper.Save(inputMemoryStreamMock.Object, fileName, timeManagementMock.Object);
// Assert
inputMemoryStreamMock.Verify(c => c.CopyTo(outputMemoryStreamMock.Object));
}
以下是测试方法:
public void Save(Stream portfolioReportFileInfoBase, string destinationName, ITimeManagement timeManagement)
{
string destinationPath = GetDestinationPath(timeManagement);
string destinationFileUri = Path.Combine(destinationPath, destinationName);
FileSystem.Directory.CreateDirectory(destinationPath);
using (var fileStream = FileSystem.File.Create(destinationFileUri))
{
portfolioReportFileInfoBase.CopyTo(fileStream);
}
}
谢谢
答案 0 :(得分:3)
我接受了几件事:
<强>可读性强>
我不确定你的测试读得非常好。为什么声明一个fileName
变量,就好像它甚至没有断言那样重要?同样适用于date
。这会使您的测试变得杂乱无章。内联值或Anonymous Variables可以提供更好的信噪比。
那你为什么要设置CreateDirectory()
来回复一些东西?你永远不会使用那个回报值,对吗?我建议你摆脱它和directoryInfoBaseMock
变量。与FileInfo
和fileInfoFactory
相同。您的测试需要包含最低限度来设置要验证的对象,仅此而已。如果你需要构建一个深度复杂的对象图来测试一个简单的东西,那么通常会出现问题。
<强>设计强>
引发警报的第二件事是Save()
混合了多个语言级别。当你阅读它时,似乎是在同一时间:
投资组合和报告
时间管理
很多低级文件系统的东西
这通常表明对象试图处理太多,违反了单一责任原则。
我要做的是将这些职责分配到不同的对象中。
为什么Save()
需要了解时间管理?当然,我们使用时间管理来计算目标路径,但是如果我们直接将目标路径直接传递给Save()
方法,它是否会更好地与语言级别和责任级别保持一致?
为什么Save()
需要了解投资组合和报告?好吧,基本没有理由。您只需将portfolioReportFileInfoBase
重命名为... stream
。
Save()
和GetPath()
移动到单独的低级文件系统包装类(IFileSystem
是一个完美的候选者),消除ReportFileHelper
和{{1}}之间的紧密耦合文件系统。
请勿尝试使用模拟测试您的报告是否已写入磁盘。通过集成测试来完成它。 Don't mock types you don't own。仅使用模拟来测试您自己的类如何相互通信。将外部库/平台包装到包装器对象中,并在应用程序的边界编写集成测试,以验证包装器是否能够很好地与这些外部库/平台配合使用。
答案 1 :(得分:1)
如果您不愿意更改Save
方法的代码,则必须使用隔离框架(如TypeMock Isolator或Microsoft Fakes)来测试此案例,因为CopyTo
不能被Moq嘲笑。
您已经封装了对IFileSystem
后面的文件系统的访问权限;为什么不添加像CopyStreamToPath
这样的方法?
public void Save(Stream portfolioReportFileInfoBase, string destinationName, ITimeManagement timeManagement)
{
string destinationPath = GetDestinationPath(timeManagement);
string destinationFileUri = Path.Combine(destinationPath, destinationName);
FileSystem.Directory.CreateDirectory(destinationPath);
FileSystem.CopyStreamToPath(portfolioReportFileInfoBase, destinationFileUri);
}
和测试:
fileSystemMock.Verify(c => c.CopyStreamToPath(inputMemoryStreamMock.object, It.IsAny<string>()));