如何模擬 Amazon S3 進行整合測試
一、簡介
在本文中,我們將學習如何模擬 Amazon S3(簡單儲存服務)來執行 Java 應用程式的整合測試。
為了展示其工作原理,我們將建立一個 CRUD(建立、讀取、更新、刪除)服務,該服務使用 AWS 開發工具包與 S3 進行互動。然後,我們將使用模擬的 S3 服務為每個操作編寫整合測試。
2.S3概述
Amazon Simple Storage Service (S3) 是 Amazon Web Services (AWS) 提供的高度可擴展且安全的雲端儲存服務。它使用物件儲存模型,允許使用者從網路上的任何位置儲存和檢索資料。
該服務可透過 REST 風格的 API 訪問,並且 AWS 為 Java 應用程式提供了一個 SDK,用於執行建立、列出和刪除 S3 儲存桶和物件等操作。
接下來,我們開始使用 AWS SDK 建立 S3 的 Java CRUD 服務,並實作建立、讀取、更新和刪除操作。
3. 示範S3 CRUD Java服務
在開始使用 S3 之前,我們需要將 AWS SDK 的依賴項新增到我們的專案中:
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
<version>2.20.52</version>
</dependency>
要查看最新版本,我們可以檢查Maven Central 。
接下來,我們使用software.amazon.awssdk.services.s3.S3Client
作為依賴項來建立S3CrudService
類別:
class S3CrudService {
private final S3Client s3Client;
public S3CrudService(S3Client s3Client) {
this.s3Client = s3Client;
}
// ...
}
現在我們已經建立了服務,讓我們使用 AWS SDK 提供的S3Client
API 來實作createBucket(), createObject(), getObject()
和deleteObject()
操作:
void createBucket(String bucketName) {
// build bucketRequest
s3Client.createBucket(bucketRequest);
}
void createObject(String bucketName, File inMemoryObject) {
// build putObjectRequest
s3Client.putObject(request, RequestBody.fromByteBuffer(inMemoryObject.getContent()));
}
Optional<byte[]> getObject(String bucketName, String objectKey) {
try {
// build getObjectRequest
ResponseBytes<GetObjectResponse> responseResponseBytes = s3Client.getObjectAsBytes(getObjectRequest);
return Optional.of(responseResponseBytes.asByteArray());
} catch (S3Exception e) {
return Optional.empty();
}
}
boolean deleteObject(String bucketName, String objectKey) {
try {
// build deleteObjectRequest
s3Client.deleteObject(deleteObjectRequest);
return true;
} catch (S3Exception e) {
return false;
}
}
現在我們已經建立了 S3 操作,讓我們學習如何使用模擬的 S3 服務來實現整合測試。
4.使用S3Mock庫進行整合測試
在本教程中,我們選擇使用 Adobe 在開源 Apache V2 許可證下提供的S3Mock庫。 S3Mock 是一個輕量級伺服器,它實作了 Amazon S3 API 最常用的操作。對於支援的S3操作,我們可以查看S3Mock儲存庫自述文件中的專用部分。
庫開發人員建議單獨運行 S3Mock 服務,最好使用提供的 Docker 容器。
按照建議,讓我們使用 Docker 和 Testcontainers 來執行 S3Mock 服務進行整合測試。
4.1.依賴關係
接下來,讓我們新增必要的依賴項以與 Testcontainers 一起執行 S3Mock:
<dependency>
<groupId>com.adobe.testing</groupId>
<artifactId>s3mock</artifactId>
<version>3.3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.adobe.testing</groupId>
<artifactId>s3mock-testcontainers</artifactId>
<version>3.3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.19.4</version>
<scope>test</scope>
</dependency>
我們可以在 Maven Central 上查看[s3mock](https://mvnrepository.com/artifact/com.adobe.testing/s3mock)
、 [s3mock-testcontainers](https://mvnrepository.com/artifact/com.adobe.testing/s3mock-testcontainers)
、 [junit-jupiter](https://mvnrepository.com/artifact/org.testcontainers/junit-jupiter)
連結來查看最新版本。
4.2.設定
作為前提條件,我們必須有一個正在運行的Docker環境,以確保測試容器能夠啟動。
當我們在整合測試類別上使用@TestConainers
和@Container
註解時,將從登錄中提取S3MockContainer
的最新 Docker 映像並在本地 Docker 環境中啟動:
@Testcontainers
class S3CrudServiceIntegrationTest {
@Container
private S3MockContainer s3Mock = new S3MockContainer("latest");
}
在執行整合測試之前,讓我們在@BeforeEach
生命週期方法中建立一個S3Client
實例:
@BeforeEach
void setUp() {
var endpoint = s3Mock.getHttpsEndpoint();
var serviceConfig = S3Configuration.builder()
.pathStyleAccessEnabled(true)
.build();
var httpClient = UrlConnectionHttpClient.builder()
.buildWithDefaults(AttributeMap.builder()
.put(TRUST_ALL_CERTIFICATES, Boolean.TRUE)
.build());
s3Client = S3Client.builder()
.endpointOverride(URI.create(endpoint))
.serviceConfiguration(serviceConfig)
.httpClient(httpClient)
.build();
}
在setup()
方法中,我們使用S3Client
介面提供的建構器初始化了S3Client
的實例。在此初始化中,我們指定了以下參數的配置:
-
endpointOverwrite
:此參數配置為定義 S3 模擬服務的位址。 -
pathStyleAccessEnabled
:我們在服務配置中將此參數設為true
。 -
**TRUST_ALL_CERTIFICATES**
:此外,我們配置了一個包含所有受信任憑證的httpClient
實例,透過將TRUST_ALL_CERTIFICATES
設為true
來指示。
4.3.為S3CrudService
編寫整合測試
當我們完成基礎設施設定後,讓我們為S3CrudService
作業編寫一些整合測試。
首先我們建立一個bucket並驗證是否建立成功:
var s3CrudService = new S3CrudService(s3Client);
s3CrudService.createBucket(TEST_BUCKET_NAME);
var createdBucketName = s3Client.listBuckets().buckets().get(0).name();
assertThat(TEST_BUCKET_NAME).isEqualTo(createdBucketName);
成功建立儲存桶後,讓我們在 S3 中上傳一個新物件。
為此,首先,我們使用FileGenerator
產生一個位元組數組,然後createObject()
方法將其作為物件保存在已建立的儲存桶中:
var fileToSave = FileGenerator.generateFiles(1, 100).get(0);
s3CrudService.createObject(TEST_BUCKET_NAME, fileToSave);
接下來,我們使用已儲存檔案的檔案名稱呼叫getObject()
方法來確認物件是否確實保存在 S3 中:
var savedFileContent = s3CrudService.getObject(TEST_BUCKET_NAME, fileToSave.getName());
assertThat(Arrays.equals(fileToSave.getContent().array(), savedFileContent)).isTrue();
最後,讓我們測試一下deleteObject()
是否也能如預期運作。首先,我們使用儲存桶名稱和目標檔案名稱來呼叫deleteObject()
方法。隨後,我們再次呼叫getObject()
並檢查結果是否為空:
s3CrudService.deleteObject(TEST_BUCKET_NAME,fileToSave.getName());
var deletedFileContent = s3CrudService.getObject(TEST_BUCKET_NAME, fileToSave.getName());
assertThat(deletedFileContent).isEmpty();
5. 結論
在本教程中,我們學習如何使用S3Mock
函式庫來模擬真實的 S3 服務,從而編寫依賴 AWS S3 服務的整合測試。
為了示範這一點,首先,我們實作了一個基本的 CRUD 服務,用於從 S3 建立、讀取和刪除物件。然後,我們使用S3Mock庫實現了整合測試。
與往常一樣,本文的完整實作可以在 GitHub 上找到。