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

How to write unit test upload directory to S3?

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

这是我的代码

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(), 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()
);