如何将单元测试上传目录写入S3?

问题描述

我有一种方法可以将目录上传到s3。 我想为此编写一个单元测试,并且一直在搜索,但是大多数测试都是集成测试。 也许我不应该对这种过程进行单元测试? 我没有使用testcontainer进行集成测试的特权,因此我不得不废弃testcontainer。

这是我的代码

public void uploadDir(final Path path,final String key) throws InternalServerError {
        final TransferManager transferManager = TransferManagerBuilder.standard()
                                                                      .withS3Client(s3Client)
                                                                      .build();
       

            final ProgressListener progressListener = progressEvent -> {
                if (progressEvent.getBytesTransferred() > 0) {
                    double percentTransferred = progressEvent.getBytesTransferred() * 100.0 / progressEvent.getBytes();
                    log.info("Transferred " + percentTransferred + "%");
                }
            };

            final MultipleFileUpload xfer = transferManager.uploadDirectory("mybucket",key,path.toFile(),true);
            xfer.addProgressListener(progressListener);
            xfer.waitForCompletion();
            
           log.info("Upload has been completed.");
        } catch (final AmazonServiceException | InterruptedException exception) {
            
            ... do something here
        } finally {
            transferManager.shutdownNow();
        }
    } 

解决方法

方法中似乎没有多少单元测试,但是参数和交互。
模拟TransferManager并不是真正的解决方案,因为您需要模拟MultipleFileUpload,并且还需要添加调用ProgressListener的代码。您最终将嘲笑所有使它无用的东西。测试广泛使用的库对您也没有太大的价值。

我将测试异常情况,这种情况在集成测试期间不太可能发生(又是不可重复的)。
大概是这样的(假设mockito):

@Test
void uploadDirFailsTest() {
  TransferManager mockTM = mock(TransferManager.class);

  doThrow(new AmazonServiceException(...)) // hopefully it has a public constructor
       .when(mockTM)
       .uploadDirectory(eq("mybucket"),any(),eq(true)); //more matching if needed
  
  YourClassName instance = new YourClassName(mockTM);

  instance.uploadDir(path,key);
  
/*
verify correct exception handling here or
if you rethrow the exception wrap the call with
assertThrows()
*/

}


public class YourClassName {
final TransferManager transferManager;

public YourClassName(TransferManager transferManager) {
  this.transferManager = transferManager;
}

public void uploadDir(final Path path,final String key) throws InternalServerError {
            final ProgressListener progressListener = progressEvent -> {
                if (progressEvent.getBytesTransferred() > 0) {
                    double percentTransferred = progressEvent.getBytesTransferred() * 100.0 / progressEvent.getBytes();
                    log.info("Transferred " + percentTransferred + "%");
                }
            };

            final MultipleFileUpload xfer = transferManager.uploadDirectory("mybucket",key,path.toFile(),true);
            xfer.addProgressListener(progressListener);
            xfer.waitForCompletion();
            
           log.info("Upload has been completed.");
        } catch (final AmazonServiceException | InterruptedException exception) {
            
            ... do something here
        } finally {
            transferManager.shutdownNow();
        }
    } 
//real use
new YourClassName(
    TransferManagerBuilder.standard() 
        .withS3Client(s3Client)
        .build()
);