AWS SDKを使っていろいろとAWSに対して処理を書くことが多いのですが、テストコードを書く際にAWSリソースとのやりとりの部分のテストコードを書くことにどうすれば良いかわからないことがあります。
今回は、AWS SDK for Java v2を使ったS3を操作するコードに対してMockitoを使ったモックによるテストコードを書いてみたのでその紹介をします。
以下のようなコードがあるとします。
public class MyS3Service { private final S3Client s3Client; public MyS3Service(S3Client s3Client) { this.s3Client = s3Client; } public void uploadData(String bucket, String key, byte[] data) { PutObjectRequest putObjectRequest = PutObjectRequest.builder() .bucket(bucket) .key(key) .build(); s3Client.putObject(putObjectRequest, RequestBody.fromBytes(data)); } public List<String> listAllBuckets() { ListBucketsResponse response = s3Client.listBuckets(); List<Bucket> bucketList = response.buckets(); return bucketList.stream().map(Bucket::name).collect(Collectors.toList()); } }
やっていることは単純で、データをS3バケットに入れたり、バケットのリストを取得することをやっています。
上記のコードに対してテストコードを実装します。
まずは必要なライブラリです。今回はGradleを使っているので、build.gradle
にて以下のように書きます。
plugins { id 'java' id 'com.diffplug.spotless' version '7.0.2' } group = 'com.github.miyohide' version = '1.0' repositories { mavenCentral() } dependencies { implementation 'software.amazon.awssdk:s3:2.30.0' testImplementation platform('org.junit:junit-bom:5.10.0') testImplementation 'org.junit.jupiter:junit-jupiter' testImplementation 'org.mockito:mockito-core:5.15.2' testImplementation 'org.mockito:mockito-junit-jupiter:5.15.2' } test { useJUnitPlatform() }
ポイントは、mokito-core
とmockito-junit-jupiter
をtestImplementation
に追加することです。あとはJUnit5を使ったテストコードと同じ。
テストコードは以下のような感じになりました。
@ExtendWith(MockitoExtension.class) class MyS3ServiceTest { @Mock private S3Client s3Client; @InjectMocks private MyS3Service myS3Service; @Test void uploadData() { String bucketName = "test-bucket"; String key = "test-key"; byte[] data = "test-data".getBytes(); myS3Service.uploadData(bucketName, key, data); PutObjectRequest expect = PutObjectRequest.builder().bucket(bucketName).key(key).build(); verify(s3Client).putObject(eq(expect), any(RequestBody.class)); } @Test void listAllBuckets() { ListBucketsResponse listBucketsResponse = ListBucketsResponse.builder() .buckets(Bucket.builder().name("test-bucket1").build()) .build(); when(s3Client.listBuckets()).thenReturn(listBucketsResponse); List<String> actual = myS3Service.listAllBuckets(); List<String> expected = Arrays.asList("test-bucket1"); assertEquals(expected, actual); } }
@Mock
でS3Client
をモック化して、@InjectMocks
でモック化したS3Client
を使うようにしています。あとは普通にテストコードを書くだけ。
Mockitoの使い方は公式サイトや解説ページを見ながら色々と実装してみました。
モックオブジェクトに対してメソッドが呼び出された時に戻り値を指定するwhen
とthenReturn
の組み合わせやverify
、eq
、any
などの使い方を知ると色々とテストが書けるかなと思います。
今回は、簡単のためにS3を対象にしました。S3でしたら以前取り上げたLocalStackなどを使ってテストを実行することができるのでMockitoを使ってテストコードを書く必要性はあまりないのですが、中にはシミュレートしにくい状況もあるので、こういう手段も取れるという例として紹介しました。
ただ、Mockitoで定義するAPIの戻り値は書いた通りにしか動かないので、AWSの仕様とは異なる可能性が高いということは大前提として認識しておく必要があります。