1

我想编写一个单元测试来检查流是否已复制到磁盘上:

问题是 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);
        }
    }

谢谢

4

2 回答 2

3

我对几件事的看法:

可读性

我不确定你的测试读起来是否很好。为什么你声明一个fileName变量,好像它很重要,而你甚至没有断言它?也一样date。这会使您的测试杂乱无章,并带有不必要的细节。内联值或匿名变量允许更好的信噪比。

那你为什么要设置CreateDirectory()退货呢?您从不使用该返回值,是吗?我建议你摆脱那个和directoryInfoBaseMock变量。与FileInfo和相同fileInfoFactory。您的测试需要包含最少的设置来设置您要验证的对象,仅此而已。如果您需要构建一个深入的、复杂的对象图来测试一件简单的事情,那么通常某个地方会出现问题。

设计

引发警报的第二件事是Save()混合多个语言级别。当您阅读它时,它似乎同时是:

  • 投资组合和报告

  • 时间管理

  • 很多低级文件系统的东西

这通常表明一个对象试图处理太多,违反了单一职责原则。

我要做的是将这些职责分配给不同的对象。

为什么Save()需要了解时间管理?Save()当然,我们使用时间管理来计算目标路径,但是如果我们直接将目标路径传递给它,它会更好地与方法的语言级别和责任级别保持一致吗?

为什么Save()需要了解投资组合和报告?嗯,基本上没有理由。你可以重命名为portfolioReportFileInfoBase......stream

Save()然后GetPath()可以移动到一个单独的较低级别的文件系统包装类(IFileSystem是一个完美的候选者),消除ReportFileHelper与文件系统之间的紧密耦合。

不要尝试使用模拟测试您的报告是否已写入磁盘。通过集成测试来做到这一点。不要模拟你不拥有的类型。仅使用模拟来测试您自己的类如何相互通信。将外部库/平台包装到包装器对象中,并在应用程序的边界编写集成测试,以验证您的包装器与这些外部库/平台兼容。

于 2013-10-04T11:57:48.453 回答
1

如果您不愿意更改Save方法的代码,则必须使用 TypeMock Isolator 或 Microsoft Fakes 之类的隔离框架来测试这种情况,因为CopyToMoq 无法模拟。

您已经封装了对文件系统的访问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>()));
于 2013-10-04T10:53:04.023 回答