繁体   English   中英

如何在我的 C# 项目中对文件上传到 Amazon S3 进行单元测试?

[英]How do I unit test that a file uploads to Amazon S3 in my C# project?

我已经开始为文件的 Amazon S3 容器编写单元测试。 我以前从未为 S3 编写过测试,我想断言文件已上传,或者至少调用了 S3 的上传方法。

因此,我要做的只是对将文件上传到 S3 容器的方法进行单元测试。 这是我迄今为止编写的用于处理此问题的测试。

注意:我在测试中使用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>());
    }

正如您将看到的,我定义了我的测试系统,我引入并模拟了 IAmazonS3 接口,我还模拟了通常在您将文件上传到 S3 时调用的TransferUtility

FileRepository类有一个文件存在检查,您将在下面的方法中看到它,这就是为什么我将带有模拟GetObjectRequestGetObjectAsync传递到FileRepository的原因。

这是我将文件上传到 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);
        }
    }
}

问题是,我如何断言该文件已上传或至少已调用UploadAsync一次?

与第 3 方依赖项(即TransferUtility )的紧密耦合是这里的设计问题

//...

var fileTransferUtility = new TransferUtility(_client);
 
//...

这使得孤立地测试您的代码变得困难。

考虑添加一个额外的抽象层来包装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
}

还要注意通过将存储库中完成的大多数检查重构到新实用程序来应用的关注点分离/单一责任原则 (SoC / SRP)

现在,这简化了存储库以专注于其核心关注点

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);
}

现在可以隔离被测对象以验证预期行为

[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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM