首页 > 解决方案 > 如何从 amazon s3 模拟/单元测试下载?

问题描述

我是单元测试/起订量的新手,想知道如何做到这一点。我有一个将文件从 s3 下载到本地的功能。我如何模拟它,使其实际上不使用 transferUtility 从 s3 下载任何内容?

bool downloadFileFromS3(string localPathToSave, string filenameToDownloadAs, string bucketName, string objectKey)
{
    try
    {
        string accessKey = "xxx";
        string secretKey = "xxx";
        // do stuff before
        TransferUtility transferUtility = new TransferUtility(accessKey, secretKey);
        transferUtility.Download( Path.Combine(localPathToSave, filenameToDownloadAs), bucketName, objectKey );
        // do stuff after
        return true;
    }

    catch (Exception e)
    {
        // stuff
    }
}

我已经创建了模拟,但我不知道如何使用它来测试我编写的函数。

[Test]
public void testDownloadFromS3()
{
    string filePath = null;
    string bucketName = null;
    string key = null;

    Mock<Amazon.S3.Transfer.TransferUtility> mock = new Mock<Amazon.S3.Transfer.TransferUtility>();
    //mock.Setup(x => x.Download(filePath, bucketName, key)).Verifiable();
    //Mock.Verify();
}

标签: c#unit-testingamazon-s3moqawss3transferutility

解决方案


根据文档TransferUtility该类实现了ITransferUtility接口。

如果您需要测试您的代码,downloadFileFromS3那么您的代码应该依赖于抽象,而不是SOLID 的 DIP所说的那样依赖于具体。

private readonly ITransferUtility transferUtility; //should be set via constructor injection

bool downloadFileFromS3(string localPathToSave, string filenameToDownloadAs, string bucketName, string objectKey)
{
    try
    {
        // do stuff before

        transferUtility.Download( Path.Combine(localPathToSave, filenameToDownloadAs), bucketName, objectKey );

        // do stuff after
        return true;
    }

    catch (Exception e)
    {
        // stuff
    }
}

因为该Download 方法不返回任何内容,所以您需要做的就是设置可验证性以防万一

[Test]
public void testDownloadFromS3_HappyPath()
{
    //Arrange
    const string localPathToSave = "C:/tmp/";
    const string filenameToDownloadAs = "test.txt";
    const string bucketName = "x";
    const string objectKey = "y";

    Mock<Amazon.S3.Transfer.ITransferUtility> transferUtilMock = new Mock<Amazon.S3.Transfer.ITransferUtility>();
    transferUtilMock
        .Setup(util => util.Download(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
        .Verifiable();

    var sut = new ClassThatContainsTheDownloadFileFromS3(transferUtilMock.Object);

    //Act
    var result = sut.downloadFileFromS3(localPathToSave, filenameToDownloadAs, bucketName, objectKey);

    //Assert
    Assert.IsTrue(result);
    transferUtilMock.Verify(util => util.Download(
            It.Is<string>(filePath => string.Equals(filePath, localPathToSave +filenameToDownloadAs, StringComparison.InvariantCultureIgnoreCase)),
            It.Is<string>(bucket => string.Equals(bucket, bucketName, StringComparison.InvariantCultureIgnoreCase)),
            It.Is<string>(key => string.Equals(key, objectKey, StringComparison.InvariantCultureIgnoreCase)),
        Times.Once);
}
  • 我们已设置模拟以接受任何参数(It.IsAny
  • 我们已将模拟实例的Object属性传递给包含downloadFileFromS3
  • 我使用了一个名称 sut,它指的是System Under Test
  • 我们已经使用It.Is来验证该Download方法是否以我们期望的方式被调用
  • 我们还将a传递Times.Once给 theVerify以确保Download已准确调用了一次。

这是您可以测试不愉快路径的方法:

[Test]
public void testDownloadFromS3_UnhappyPath()
{
    //Arrange
    const string localPathToSave = "C:/tmp/";
    const string filenameToDownloadAs = "test.txt";
    const string bucketName = "x";
    const string objectKey = "y";

    Mock<Amazon.S3.Transfer.ITransferUtility> transferUtilMock = new Mock<Amazon.S3.Transfer.ITransferUtility>();
    transferUtilMock
        .Setup(util => util.Download(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
        .Throws<System.IO.IOException>();;

    var sut = new ClassThatContainsTheDownloadFileFromS3(transferUtilMock.Object);

    //Act
    var result = sut.downloadFileFromS3(localPathToSave, filenameToDownloadAs, bucketName, objectKey);

    //Assert
    Assert.IsFalse(result);
    transferUtilMock.Verify(util => util.Download(
            It.Is<string>(filePath => string.Equals(filePath, localPathToSave +filenameToDownloadAs, StringComparison.InvariantCultureIgnoreCase)),
            It.Is<string>(bucket => string.Equals(bucket, bucketName, StringComparison.InvariantCultureIgnoreCase)),
            It.Is<string>(key => string.Equals(key, objectKey, StringComparison.InvariantCultureIgnoreCase)),
        Times.Once);
}
  • 如您所见,我们已将调用替换为在Verifiable调用时Throws抛出异常
  • 我也更换了Assert.IsTruetoAssert.IsFalse
    • 我假设在异常// stuff返回的情况下false

推荐阅读