[英]How do I unit test that a file uploads to Amazon S3 in my C# project?
I've started writing unit tests for an Amazon S3 container for files.我已经开始为文件的 Amazon S3 容器编写单元测试。 I've never written tests for S3 before and I want to assert that a file is uploaded or at the very least the upload method for S3 is called.
我以前从未为 S3 编写过测试,我想断言文件已上传,或者至少调用了 S3 的上传方法。
So, all I'm attempting to do is unit test a method that uploads a file to an S3 container.因此,我要做的只是对将文件上传到 S3 容器的方法进行单元测试。 Here is the test I have written so far to handle this.
这是我迄今为止编写的用于处理此问题的测试。
Note: I am using NSubstitue for mocking in my tests.注意:我在测试中使用NSubstitue进行模拟。
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.S3.Transfer;
using AmazonStorage.Repositories;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NSubstitute;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace AmazonStorage.Tests.Repositories
{
[TestClass]
public class FileRepositoryTests
{
private readonly FileRepository _sut;
private readonly IAmazonS3 _client = Substitute.For<IAmazonS3>();
private readonly TransferUtility _transfer = Substitute.For<TransferUtility>();
public FileRepositoryTests()
{
var request = new GetObjectRequest()
{
Key = "somefile.txt",
BucketName = "aws-s3-apadmi"
};
_client.GetObjectAsync(request);
_sut = new FileRepository(_client);
}
[TestMethod]
public async Task PutFileInStorage_ShouldReceieveCallToUploadAsyc()
{
// Arrange
await _transfer.UploadAsync(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<CancellationToken>());
//Assert
// I was thinking of something like this?
// await _client.Received(1).UploadObjectFromStreamAsync(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<Stream>());
}
As you'll see I define my system under test, I bring in and mock the IAmazonS3 interface, I also mock the TransferUtility
which is typically called when you upload a file to S3.正如您将看到的,我定义了我的测试系统,我引入并模拟了 IAmazonS3 接口,我还模拟了通常在您将文件上传到 S3 时调用的
TransferUtility
。
The FileRepository
class has a file exists check which you'll see below in the method and that is why I pass the GetObjectAsync
with a mocked GetObjectRequest
into the FileRepository
. FileRepository
类有一个文件存在检查,您将在下面的方法中看到它,这就是为什么我将带有模拟GetObjectRequest
的GetObjectAsync
传递到FileRepository
的原因。
Here is my method to upload a file to S3.这是我将文件上传到 S3 的方法。
private readonly IAmazonS3 _client;
public FileRepository(IAmazonS3 client)
{
_client = client;
}
public async Task PutFileInStorage(string fileName, string content)
{
try
{
await UploadToS3(fileName, content);
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
public async Task UploadToS3(string fileName, string content)
{
using MemoryStream ms = new(Encoding.UTF8.GetBytes(content));
var uploadRequest = new TransferUtilityUploadRequest
{
InputStream = ms,
Key = fileName,
BucketName = "aws-s3-apadmi"
};
bool fileCheck = await CheckFileExists(uploadRequest.Key, uploadRequest.BucketName);
if (fileCheck.Equals(true))
throw new Exception("File already exists");
if (!fileCheck)
{
// Upload file to storage
try
{
var fileTransferUtility = new TransferUtility(_client);
await fileTransferUtility.UploadAsync(uploadRequest);
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
}
The question is, how do I assert that this file has uploaded or at the very least that the UploadAsync
has been called once?问题是,我如何断言该文件已上传或至少已调用
UploadAsync
一次?
Tight coupling to 3rd party dependencies (ie TransferUtility
) is the design issue here与第 3 方依赖项(即
TransferUtility
)的紧密耦合是这里的设计问题
//...
var fileTransferUtility = new TransferUtility(_client);
//...
This makes testing your code in isolation difficult.这使得孤立地测试您的代码变得困难。
Consider adding an additional layer of abstraction that wraps TransferUtility
to give you more control over your code.考虑添加一个额外的抽象层来包装
TransferUtility
以让您更好地控制您的代码。
public interface ITransferUtility {
Task UploadAsync(TransferUtilityUploadRequest request);
}
public class FileTransferUtility : ITransferUtility {
private readonly IAmazonS3 client;
public FileTransferUtility (IAmazonS3 client) {
this.client = client;
}
public async Task UploadAsync(TransferUtilityUploadRequest uploadRequest) {
bool fileCheck = await CheckFileExists(uploadRequest.Key, uploadRequest.BucketName);
if (fileCheck)
throw new Exception("File already exists");
// Upload file to storage
try {
TransferUtility fileTransferUtility = new TransferUtility(client);
await fileTransferUtility.UploadAsync(uploadRequest);
}
catch (Exception ex) {
throw new Exception(ex.Message);
}
}
//...CheckFileExists and other necessary members
}
Also note the Separation of Concerns / Single Responsibility Principle (SoC / SRP) applied by refactoring most of the checks done in the repository to the new utility还要注意通过将存储库中完成的大多数检查重构到新实用程序来应用的关注点分离/单一责任原则 (SoC / SRP)
This now simplifies the repository to focus on its core concern现在,这简化了存储库以专注于其核心关注点
private readonly ITransferUtility fileTransferUtility;
public FileRepository(ITransferUtility fileTransferUtility) {
this.fileTransferUtility = fileTransferUtility;
}
public async Task PutFileInStorage(string fileName, string content) {
try {
await UploadToS3(fileName, content);
} catch (Exception ex) {
throw new Exception(ex.Message);
}
}
private async Task UploadToS3(string fileName, string content)
using MemoryStream ms = new(Encoding.UTF8.GetBytes(content));
TransferUtilityUploadRequest uploadRequest = new TransferUtilityUploadRequest {
InputStream = ms,
Key = fileName,
BucketName = "aws-s3-apadmi"
};
await fileTransferUtility.UploadAsync(uploadRequest);
}
The subject under test is now able to be isolated to verify expected behavior现在可以隔离被测对象以验证预期行为
[TestClass]
public class FileRepositoryTests {
private readonly FileRepository sut;
private readonly ITransferUtility transfer = Substitute.For<ITransferUtility>();
public FileRepositoryTests() {
sut = new FileRepository(transfer);
}
[TestMethod]
public async Task PutFileInStorage_ShouldReceieveCallToUploadAsyc()
//Arrange
transfer.UploadAsync(Arg.Any<TransferUtilityUploadRequest>()).Returns(Task.CompletedTask);
string fileName = "somefile.txt";
string bucketName = "aws-s3-apadmi";
string content = "Hello World";
//Act
await sut.PutFileInStorage(fileName, content);
//Assert
transfer.Received().UploadAsync(Arg.Is<TransferUtilityUploadRequest>(x =>
x.Key == fileName
&& x.BucketName = bucketName
));
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.