I am new to unit testing/moq and was wondering how this could be done. I have a function that downloads a file from s3 to local. How can I mock this so that it doesn't actually use transferUtility to download anything from 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
}
}
I've created the mock but I don't know how to use it for testing the function that I wrote.
[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();
}
According to the documentation the TransferUtility
class implements the ITransferUtility
interface.
If you need to test your downloadFileFromS3
then your code should depend on abstraction, not on concretions as the SOLID's DIP says.
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
}
}
Because the Download
method does not return anything all you need to do is to setup verifyability in case of happy path
[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);
}
Object
property to the class which contains the downloadFileFromS3
Download
method has been called in the way we expectTimes.Once
to the Verify
to make sure that the Download
has been called exactly once.This is how you can test the unhappy path:
[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
call to Throws
to throw exception whenever it is calledAssert.IsTrue
to Assert.IsFalse
// stuff
returns false