1. ã¯ããã«
AWSãå©çšããã¢ããªã±ãŒã·ã§ã³ãéçºããéãã³ãŒããæ£ããåäœãããã©ããããã¹ãããããšã¯éåžžã«éèŠã§ããããããå®éã®AWSã¯ã©ãŠãç°å¢ã«æ¥ç¶ããŠãã¹ããè¡ããšãæéãã³ã¹ãããããã ãã§ãªãããªãœãŒã¹ã«äºæãã¬å€æŽãå ããŠããŸããªã¹ã¯ããããŸãã
ããã§åœ¹ç«ã€ã®ããã¢ãã¯ããšããææ³ã§ããã¢ãã¯ãšã¯ãæ¬æ¥ã®ãµãŒãã¹ããªããžã§ã¯ãã®åäœãæš¡å£ãããã¹ãçšã®ãããŒãªããžã§ã¯ãã䜿çšããæ¹æ³ã§ããããã«ãããå®éã®AWSãµãŒãã¹ã«æ¥ç¶ããã«ã³ãŒããæ€èšŒã§ããéçºå¹çãå€§å¹ ã«åäžãããããšãã§ããŸãã
æ¬èšäºã§ã¯ãJava ã§åºã䜿ãããŠããã¢ãã¯ã©ã€ãã©ãªã§ããMockito ã䜿çšããŠãAWS SDK ãå©çšãããã¹ãææ³ã解説ããŸããå ·äœçã«ã¯ãDynamoDB ãS3 ãäŸã«æããŠãã¢ãã¯ã䜿ã£ãããŒã¿ã®ä¿åã»ååŸæäœã®ãã¹ãã®æžãæ¹ã説æããŸãã
2. ã¢ãã¯ã®æŽ»çš
2.1 ã¢ãã¯ãšã¯
ã¢ãã¯ãšã¯ãæ¬æ¥ã®ãµãŒãã¹ããªããžã§ã¯ãã®åäœãæš¡å£ãããã¹ãçšã®ãããŒãªããžã§ã¯ãã§ãããã¹ãæã«å€éšã·ã¹ãã ãè€éãªäŸåé¢ä¿ãæã€ãªããžã§ã¯ãã®æ¯ãèããã·ãã¥ã¬ãŒãããããã«äœ¿çšãããŸããAWS SDK ã®åäœãæš¡å£ãããããŒãªããžã§ã¯ããå©çšãããšããããã¯ãŒã¯æ¥ç¶ãå®éã®AWSãªãœãŒã¹ã䜿çšããã«ãAWSãµãŒãã¹ãå©çšããã³ãŒãã®ãã¹ããå¯èœã«ãªããŸãã
2.2 ã¢ãã¯ãå©çšããå©ç¹
ã¢ãã¯ãå©çšããããšã§ã以äžã®ãããªå©ç¹ãåŸãããŸãã
- ã³ã¹ãåæžïŒAWSãªãœãŒã¹ãå®éã«äœ¿çšããªãããããã¹ãæã®è²»çšãæããããŸã
- é床åäžïŒããŒã«ã«ç°å¢ã§ãã¹ããå®çµããããããã¹ãã®å®è¡ãéããªããŸã
- å®å®æ§ïŒãããã¯ãŒã¯ã®ç¶æ ãAWSãµãŒãã¹ã®äžå®å®ãã«åœ±é¿ããããå®å®ããŠãã¹ããå®è¡ã§ããŸã
2.3 Mockito ãšã¯
Mockito ã¯ãJavaã®ãŠããããã¹ãã§åºã䜿çšãããŠããã¢ãã¯ãã¬ãŒã ã¯ãŒã¯ã§ãããã¹ã察象ã®ã³ãŒããäŸåãããªããžã§ã¯ããå€éšãµãŒãã¹ã®æ¯ãèããæš¡å£ããå¶åŸ¡ããããšãã§ããŸãã
Mockito ã®äž»ãªç¹åŸŽã¯ä»¥äžã®éãã§ãã
- ç°¡åãªäœ¿ç𿹿³ïŒçŽæçãªAPIãæäŸããŠãããã¢ãã¯ãªããžã§ã¯ãã®äœæãæ¯ãèãã®å®çŸ©ã容æã§ã
- æ€èšŒæ©èœïŒã¢ãã¯ãªããžã§ã¯ãã®ã¡ãœããåŒã³åºããæ€èšŒããæåŸ éãã®çžäºäœçšãè¡ããããã確èªã§ããŸã
- JUnitçµ±åïŒJUnitãšç°¡åã«çµ±åã§ãããã¹ãã³ãŒãã®å¯èªæ§ãšä¿å®æ§ãåäžãããŸã
Mockito ã䜿çšããããšã§ãå€éšäŸåæ§ïŒäŸãã°AWS SDKãªã©ïŒãæã€ã³ãŒãã®åäœãã¹ãã容æã«ãªãããã¹ãã®å質ãšéçºå¹çãåäžãããããšãã§ããŸãã
3. ãã¹ãç°å¢ã®æºå
3.1 å¿ èŠãªããŒã«ãšã©ã€ãã©ãª
- JVMïŒAmazon Corretto 17 ã䜿çšããŸã
- ãã«ãããŒã«ïŒGradle ã䜿çšããŸã
- ãã¹ããã¬ãŒã ã¯ãŒã¯ïŒJUnit 5 ã䜿çšããŸã
- ã¢ãã¯ã©ã€ãã©ãªïŒMockito ã䜿çšããŸã
å®éã«æ€èšŒããéã®ç°å¢ã¯ä»¥äžãšãªããŸãã
- GradleïŒ8.2
- JVMïŒ17.0.10 (Amazon.com Inc. 17.0.10+7-LTS)
- OSïŒWindows 10 10.0 amd64
3.2 Gradleã®èšå®
build.gradle ãã¡ã€ã«ã«ä»¥äžã®äŸåé¢ä¿ã远å ããŸããããŒãžã§ã³ã¯ææ°ã®å®å®çã䜿çšããŠãã ããã
dependencies {
// AWS SDK for DynamoDB
implementation 'software.amazon.awssdk:dynamodb:2.28.1'
// AWS SDK for S3
implementation 'software.amazon.awssdk:s3:2.28.1'
// JUnit 5
testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0'
// Mockito
testImplementation 'org.mockito:mockito-core:5.13.0'
testImplementation 'org.mockito:mockito-junit-jupiter:5.13.0'
}
3.3 ãã¹ãã®å®è¡èšå®
Gradle ã§JUnit5 ã®ãã¹ããå®è¡ãããããbuild.gradleã«ä»¥äžã远å ããŸãã
test {
useJUnitPlatform()
}
4. DynamoDBã®æäœãã¢ãã¯åãã
4.1 Amazon DynamoDB ãšã¯
Amazon DynamoDBã¯ãAWSãæäŸãããã«ãããŒãžãã®NoSQLããŒã¿ããŒã¹ãµãŒãã¹ã§ããé«ãã¹ã±ãŒã©ããªãã£ãšäœã¬ã€ãã³ã·ãŒã§ããŒã¿ã®èªã¿æžããå¯èœã§ãã
4.2 ãµã³ãã«ã³ãŒãã®äœæ
DynamoDB ã«ããŒã¿ãä¿åã»ååŸããã¯ã©ã¹ãDynamoDbServiceããäœæããŸãã
DynamoDbClient ã«ãŠDynamoDB ãæäœããããããã®ãªããžã§ã¯ããã¢ãã¯ãã察象ãšãªããŸãã
ã¢ãã¯ãªããžã§ã¯ããæ³šå
¥ããããã®ã³ã³ã¹ãã©ã¯ã¿ãã¢ããªã§å®éã«äœ¿ãããã«DynamoDbClient ãnew ããã³ã³ã¹ãã©ã¯ã¿ã®2çš®é¡çšæããŸãã
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.GetItemRequest;
import software.amazon.awssdk.services.dynamodb.model.GetItemResponse;
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest;
import java.util.Map;
public class DynamoDbService {
private final DynamoDbClient dynamoDbClient;
/**
* ã¢ãã¯çšã³ã³ã¹ãã©ã¯ã¿.
* @param dynamoDbClient DynamoDBã¯ã©ã€ã¢ã³ã
*/
public DynamoDbService(DynamoDbClient dynamoDbClient) {
this.dynamoDbClient = dynamoDbClient;
}
/**
* ã³ã³ã¹ãã©ã¯ã¿.
* @param region AWSãªãŒãžã§ã³
*/
public DynamoDbService(Region region) {
this(DynamoDbClient.builder().region(region).build());
}
/**
* ã¢ã€ãã ãä¿åããŸã.
* @param tableName ããŒãã«å
* @param item ä¿åããã¢ã€ãã
*/
public void putItem(String tableName, Map<String, AttributeValue> item) {
PutItemRequest request = PutItemRequest.builder()
.tableName(tableName)
.item(item)
.build();
dynamoDbClient.putItem(request);
}
/**
* ã¢ã€ãã ãååŸããŸã.
* @param tableName ããŒãã«å
* @param key ååŸããã¢ã€ãã ã®ããŒ
* @return ååŸããã¢ã€ãã
*/
public Map<String, AttributeValue> getItem(String tableName, Map<String, AttributeValue> key) {
GetItemRequest request = GetItemRequest.builder()
.tableName(tableName)
.key(key)
.build();
GetItemResponse response = dynamoDbClient.getItem(request);
return response.item();
}
}
4.3 ãã¹ãã®äœæ
Mockito ã®ã¢ãããŒã·ã§ã³ã䜿çšããŠãã¢ãã¯ãªããžã§ã¯ããæ³šå
¥ããŸãã
JUnit ã®ã¢ãµãŒã·ã§ã³ãçšããŠæå³ããç¶æ
ãšãªã£ãŠããããã¹ãããŸãã
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.GetItemRequest;
import software.amazon.awssdk.services.dynamodb.model.GetItemResponse;
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest;
import java.util.HashMap;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
public class DynamoDbServiceTest {
@Mock
private DynamoDbClient dynamoDbClient;
@InjectMocks
private DynamoDbService dynamoDbService;
@Test
void ããŒã¿ãä¿åã§ããããš() {
/* æºå */
String tableName = "TestTable";
Map<String, AttributeValue> item = new HashMap<>();
item.put("id", AttributeValue.builder().s("123").build());
item.put("name", AttributeValue.builder().s("ãã¹ãããŒã¿").build());
/* å®è¡ */
dynamoDbService.putItem(tableName, item);
/* æ€èšŒ */
PutItemRequest expectedRequest = PutItemRequest.builder()
.tableName(tableName)
.item(item)
.build();
// verifyã䜿çšããŠãæåŸ
ãããªã¯ãšã¹ãã§putItemãåŒã°ããã確èª
verify(dynamoDbClient).putItem(expectedRequest);
}
@Test
void ããŒã¿ãååŸã§ããããš() {
/* æºå */
String tableName = "TestTable";
Map<String, AttributeValue> key = new HashMap<>();
key.put("id", AttributeValue.builder().s("123").build());
Map<String, AttributeValue> expectedItem = new HashMap<>();
expectedItem.put("id", AttributeValue.builder().s("123").build());
expectedItem.put("name", AttributeValue.builder().s("ãã¹ãããŒã¿").build());
GetItemResponse mockResponse = GetItemResponse.builder()
.item(expectedItem)
.build();
// whenã䜿çšããŠãgetItemã¡ãœãããåŒã°ãããšãã«mockResponseãè¿ãããã«èšå®
when(dynamoDbClient.getItem(any(GetItemRequest.class))).thenReturn(mockResponse);
/* å®è¡ */
Map<String, AttributeValue> actualItem = dynamoDbService.getItem(tableName, key);
/* æ€èšŒ */
GetItemRequest expectedRequest = GetItemRequest.builder()
.tableName(tableName)
.key(key)
.build();
// verifyã䜿çšããŠãæåŸ
ãããªã¯ãšã¹ãã§getItemãåŒã°ããã確èª
verify(dynamoDbClient).getItem(expectedRequest);
// ååŸããã¢ã€ãã ãæåŸ
éãã確èª
assertEquals(expectedItem, actualItem);
}
}
4.4 解説
- ã³ã³ã¹ãã©ã¯ã¿ã®åé¢ïŒ
ã¢ãã¯çšãšå®éã«äœ¿çšããããã®ã³ã³ã¹ãã©ã¯ã¿ãåããŠããŸããã¢ãã¯çšã³ã³ã¹ãã©ã¯ã¿ã§ã¯DynamoDbClientãçŽæ¥åãåããå®éã®ã³ã³ã¹ãã©ã¯ã¿ã§ã¯Regionãåãåã£ãŠã¯ã©ã€ã¢ã³ããæ§ç¯ããŸãã - ã¢ãã¯ã®èšå®ïŒ
@Mockãš@InjectMocksã䜿çšããŠãã¢ãã¯ãªããžã§ã¯ããæ³šå ¥ããŸãã- æ»ãå€ãèšå®ããå¿ èŠã®ãªãã¡ãœããã®å Žåãã¢ãã¯ãªããžã§ã¯ããæ³šå ¥ããã ãã§ä»ã®èšå®ã¯èŠããŸããã
- æ»ãå€ãèšå®ãããå Žåã
whenã䜿çšããŠã¡ãœããã®åŒã³åºãæã«ã¢ãã¯ã®ã¬ã¹ãã³ã¹ãè¿ãããã«èšå®ããŸãã
- æ€èšŒïŒ
verifyã䜿çšããŠãæåŸ ãããªã¯ãšã¹ãã§AWS SDK ã®ã¡ãœãããåŒã°ããã確èªããŸããassertEqualsã§æ»ãå€ãæåŸ éãã§ããããšã確èªããŸãã
5. S3 ã®æäœãã¢ãã¯åãã
5.1 Amazon S3 ãšã¯
Amazon S3 ã¯ãAWS ãæäŸãããªããžã§ã¯ãã¹ãã¬ãŒãžãµãŒãã¹ã§ãã倧容éã®ããŒã¿ãå®å šãã€é«ãå¯çšæ§ã§ä¿åã§ããŸãã
5.2 ãµã³ãã«ã³ãŒãã®äœæ
S3ã«ãã¡ã€ã«ãã¢ããããŒãã»ããŠã³ããŒãããã¯ã©ã¹ãS3ServiceããäœæããŸãã
S3Client ã«ãŠS3 ãæäœããããããã®ãªããžã§ã¯ããã¢ãã¯ãã察象ãšãªããŸãã
ã¢ãã¯ãªããžã§ã¯ããæ³šå
¥ããããã®ã³ã³ã¹ãã©ã¯ã¿ãã¢ããªã§å®éã«äœ¿ãããã«S3Client ãnew ããã³ã³ã¹ãã©ã¯ã¿ã®2çš®é¡çšæããŸãã
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.core.sync.ResponseTransformer;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
public class S3Service {
private final S3Client s3Client;
/**
* ã¢ãã¯çšã³ã³ã¹ãã©ã¯ã¿.
* @param s3Client S3ã¯ã©ã€ã¢ã³ã
*/
public S3Service(S3Client s3Client) {
this.s3Client = s3Client;
}
/**
* ã³ã³ã¹ãã©ã¯ã¿.
* @param region AWSãªãŒãžã§ã³
*/
public S3Service(Region region) {
this(S3Client.builder().region(region).build());
}
/**
* ããŒã¿ãS3ãžã¢ããããŒãããŸã.
* @param bucketName ãã±ããå
* @param key ãªããžã§ã¯ãããŒ
* @param data ã¢ããããŒãããããŒã¿
*/
public void uploadData(String bucketName, String key, byte[] data) {
PutObjectRequest request = PutObjectRequest.builder()
.bucket(bucketName)
.key(key)
.build();
s3Client.putObject(request, RequestBody.fromBytes(data));
}
/**
* ãã¡ã€ã«ãS3ããããŠã³ããŒãããbyteé
åãšããŠè¿ããŸã.
* @param bucketName ãã±ããå
* @param key ãªããžã§ã¯ãããŒ
* @return ããŠã³ããŒããããã¡ã€ã«ã®byteé
å
*/
public byte[] downloadFile(String bucketName, String key) {
GetObjectRequest request = GetObjectRequest.builder()
.bucket(bucketName)
.key(key)
.build();
return s3Client.getObject(request, ResponseTransformer.toBytes()).asByteArray();
}
}
5.3 ãã¹ãã®äœæ
Mockito ã®ã¢ãããŒã·ã§ã³ã䜿çšããŠãã¢ãã¯ãªããžã§ã¯ããæ³šå
¥ããŸãã
JUnit ã®ã¢ãµãŒã·ã§ã³ãçšããŠæå³ããç¶æ
ãšãªã£ãŠããããã¹ãããŸãã
ããã«ãäŸå€ãçºçããå Žåã®ãã¹ãã远å ããŸãã
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentMatchers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import software.amazon.awssdk.core.ResponseBytes;
import software.amazon.awssdk.core.exception.SdkClientException;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.core.sync.ResponseTransformer;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
public class S3ServiceTest {
@Mock
private S3Client s3Client;
@InjectMocks
private S3Service s3Service;
@Test
void ãã¡ã€ã«ãã¢ããããŒãã§ããããš() {
/* æºå */
String bucketName = "test-bucket";
String key = "test.txt";
byte[] data = "ãã¹ãããŒã¿".getBytes();
/* å®è¡ */
s3Service.uploadData(bucketName, key, data);
/* æ€èšŒ */
PutObjectRequest expectedRequest = PutObjectRequest.builder()
.bucket(bucketName)
.key(key)
.build();
// verifyã䜿çšããŠãæåŸ
ãããªã¯ãšã¹ãã§putObjectãåŒã°ããã確èª
verify(s3Client).putObject(eq(expectedRequest), any(RequestBody.class));
}
@Test
void ãã¡ã€ã«ãããŠã³ããŒãã§ããããš() {
/* æºå */
String bucketName = "test-bucket";
String key = "test.txt";
byte[] expectedData = "ãã¹ãããŒã¿".getBytes();
// ã¢ãã¯ã®ã¬ã¹ãã³ã¹ãäœæ
ResponseBytes<GetObjectResponse> mockResponse = ResponseBytes.fromByteArray(GetObjectResponse.builder().build(), expectedData);
// whenã䜿çšããŠãgetObjectã¡ãœãããåŒã°ãããšãã«mockResponseãè¿ãããã«èšå®
when(s3Client.getObject(any(GetObjectRequest.class), ArgumentMatchers.<ResponseTransformer<GetObjectResponse, ResponseBytes<GetObjectResponse>>>any()))
.thenReturn(mockResponse);
/* å®è¡ */
byte[] actualData = s3Service.downloadFile(bucketName, key);
/* æ€èšŒ */
GetObjectRequest expectedRequest = GetObjectRequest.builder()
.bucket(bucketName)
.key(key)
.build();
// verifyã䜿çšããŠãæåŸ
ãããªã¯ãšã¹ãã§getObjectãåŒã°ããã確èª
verify(s3Client).getObject(eq(expectedRequest), ArgumentMatchers.<ResponseTransformer<GetObjectResponse, ResponseBytes<GetObjectResponse>>>any());
// ããŠã³ããŒãããããŒã¿ãæåŸ
éãã確èª
assertArrayEquals(expectedData, actualData);
}
@Test
void ã¢ããããŒãæã«äŸå€ãçºçããå Žåã®ãã¹ã() {
/* æºå */
// doThrowã䜿çšããŠãputObjectã¡ãœãããåŒã°ãããšãã«äŸå€ãã¹ããŒããããã«èšå®
doThrow(SdkClientException.create("ãšã©ãŒ"))
.when(s3Client).putObject(any(PutObjectRequest.class), any(RequestBody.class));
/* å®è¡ãšæ€èšŒ */
assertThrows(SdkClientException.class, () -> {
s3Service.uploadData("bucket", "key", "ãã¹ãããŒã¿".getBytes());
});
}
}
5.4 解説
DynamoDB ã®äŸã«å ããäŸå€ãçºçããéã®ãã¹ãã±ãŒã¹ã远å ããŠããŸãã
doThrow ãå©çšããŠputObject ãå®è¡ããéã«SdkClientException ãçºçããããã«èšå®ããŠããŸãã
assertThrows ãå©çšããŠãå®éã«s3Service.uploadData ãå®è¡ããéã«SdkClientException ãçºçããããšã確èªããŠããŸãã
6. ã¢ãã¯ã䜿çšããéã®æ³šæç¹
ã¢ãã¯ãå©çšããããšã§å¹ççãªãã¹ããå¯èœã«ãªããŸãããæ£ç¢ºãã€ä¿¡é Œæ§ã®é«ããã¹ããè¡ãããã«ã¯ããã€ãã®æ³šæç¹ããããŸããããã§ã¯ãå ·äœäŸãæããªããæ³šæãã¹ããã€ã³ãã解説ããŸãã
6.1 SDK ã®ä»æ§ãååã«çè§£ããããš
ã¢ãã¯ã䜿ã£ãŠãã¹ããè¡ãéã«ã¯ãAWS SDK ã®ä»æ§ãå¶çŽãæ£ããçè§£ããŠããªããšã誀ã£ããã¹ããæžãããšã«ãªããŸããã¢ãã¯ã¯æ¬çªç°å¢ã®AWSãµãŒãã¹ãã·ãã¥ã¬ãŒãããããã®ããŒã«ã§ãããAWSãµãŒãã¹ã®ç¹å®ã®å¶çŽãåäœãç¥ããã«ãã¹ããäœæãããšãå®éã®æåãšç°ãªãçµæãåºãå¯èœæ§ããããŸãããã®ãããAWS SDK ã®ä»æ§ãååã«çè§£ããŠãã¹ããèšèšããããšãéèŠã§ãã
äŸïŒDynamoDB ã®BatchWriteItem ã«ããã25ä»¶ã®å¶é
DynamoDB ã®BatchWriteItemAPI ã«ã¯ã1åã®ãªã¯ãšã¹ãã§æå€§25ä»¶ã®ã¢ã€ãã ããæžã蟌ããªããšããå¶éããããŸãã(ãã詳现ãªå¶çŽã¯å
¬åŒããã¥ã¡ã³ã åç
§)
ãããã®å¶çŽãçè§£ãããã¢ãã¯ã䜿ã£ããã¹ãã§å€§éã®ããŒã¿ãäžåºŠã«æžã蟌ãåŠçããã¹ãããå Žåãå®éã®ç°å¢ã§ãã®å¶éã«ãããšã©ãŒãçºçããå¯èœæ§ããããŸãã
誀ã£ãã¢ãã¯ã®ãã¹ãäŸ
以äžã¯ãDynamoDB ã«å¯ŸããŠå€§éã®ã¢ã€ãã ãäžåºŠã«æžã蟌ããã¹ãã§ããã25ä»¶ã®å¶éãç¡èŠããŠããŸãããã®ãã¹ãã¯ã¢ãã¯ã䜿ã£ãŠãããããšã©ãŒãçºçããæåããŸãããå®éã®AWSç°å¢ã§ã¯ããå¯èœæ§ããããŸãã
@Test
void 倧éã®ããŒã¿ããããæžã蟌ã¿ã§ããããš() {
/* æºå */
List<WriteRequest> items = generateItems(30); // 25ä»¶ãè¶
ããã¢ã€ãã
/* å®è¡ */
// dynamoDbServiceã«ãŠåå²ããŠæžãèŸŒãæ©èœããªãå Žåãå®éã®ç°å¢ã§ã¯25件以äžã®ã¢ã€ãã ããããšãšã©ãŒã«ãªã
dynamoDbService.batchWriteItems("TestTable", items);
/* æ€èšŒ */
verify(dynamoDbClient).batchWriteItem(any(BatchWriteItemRequest.class));
}
ãã®ãã¹ãã¯ãã¢ãã¯ç°å¢ã§ã¯æåããŸãããDynamoDB ã®å¶éãçè§£ããŠããªããããå®éã®ç°å¢ã§25ä»¶ãè¶ ãããªã¯ãšã¹ãããšã©ãŒã«ãªãããšã«æ°ã¥ãããšãã§ããŸããããã¹ããè¡ãéã¯ã以äžã®ãããªãã¹ããå¿ èŠã§ãã
@Test
void DynamoDBã®ãããæžã蟌ã¿å¶éãèæ
®ãããã¹ã() {
/* æºå */
List<WriteRequest> items = generateItems(30); // 25ä»¶ãè¶
ããã¢ã€ãã
/* å®è¡ */
dynamoDbService.batchWriteItems("TestTable", items);
/* æ€èšŒ */
// 25ä»¶ãã€2åã«åããŠåŒã³åºãããããšã確èª
verify(dynamoDbClient, times(2)).batchWriteItem(any(BatchWriteItemRequest.class));
}
ãã®ãã¹ãã§ã¯ã30ä»¶ã®ã¢ã€ãã ãçæããdynamoDbService.batchWriteItems ã¡ãœãããåŒã³åºããŸãããããŠãDynamoDB ã¯ã©ã€ã¢ã³ãã®batchWriteItem ã¡ãœããã2ååŒã³åºãããããšãæ€èšŒããŸããããã«ããã25ä»¶ã®å¶éãèæ
®ããŠé©åã«åå²ãããŠããããšã確èªã§ããŸãã
6.2 å®éã®AWSãµãŒãã¹ãšã®éããæèãã
ã¢ãã¯ã䜿ã£ããã¹ãã§ã¯ãå®éã®AWSãµãŒãã¹ãšç°ãªãéšåãååšããããšã«æ³šæãå¿ èŠã§ããç¹ã«ãããã©ãŒãã³ã¹ãã¹ã«ãŒãããã®é¢ã§ã¯ã¢ãã¯ã§ã¯æ€èšŒã§ããªãå Žåãå€ããããè² è·ãã¹ããããã©ãŒãã³ã¹ãã¹ãã¯æ¬çªç°å¢ã«è¿ãæ¡ä»¶ã§è¡ãå¿ èŠããããŸãã
äŸïŒDynamoDB ã®èªã¿æžãã¹ã«ãŒããã
DynamoDB ã§ã¯ããããžã§ãã³ã°ãããã¹ã«ãŒãããïŒèªã¿èŸŒã¿ãæžã蟌ã¿ã®ãã£ãã·ãã£ïŒããããäžå®ã®å¶éãè¶ ãããšãªã¯ãšã¹ããã¹ããããªã³ã°ãããŸããã¢ãã¯ã§ã¯ãã®ã¹ããããªã³ã°ãåçŸã§ããªãããã倧éã®ããŒã¿ãæ±ãããã©ãŒãã³ã¹ãã¹ãã¯å®éã®ç°å¢ã§è¡ãå¿ èŠããããŸãã
7. ãŸãšã
Mockito ã䜿çšããŠAWS SDKã®ã¯ã©ã€ã¢ã³ããã¢ãã¯åããããšã§ãå¹ççãã€å®å šã«åäœãã¹ããè¡ãããšãã§ããŸããäŸåæ§ã®æ³šå ¥ãMockito ã®ã¢ãããŒã·ã§ã³ãé©åã«äœ¿çšããAWSãµãŒãã¹ã®ç¹æã®å¶çŽãçè§£ããããšã§ãä¿¡é Œæ§ã®é«ããã¹ããå®çŸã§ããŸãã
7.1 äŸåæ§ã®æ³šå ¥ã䜿çšããŠAWSã¯ã©ã€ã¢ã³ããã¢ãã¯åãã
ã³ã³ã¹ãã©ã¯ã¿ã€ã³ãžã§ã¯ã·ã§ã³ã䜿çšããAWSã¯ã©ã€ã¢ã³ããã¢ãã¯å¯èœã«ããŸããå®éã®ç°å¢çšãšãã¹ãçšã®2ã€ã®ã³ã³ã¹ãã©ã¯ã¿ãçšæããŸãã
public class S3Service {
private final S3Client s3Client;
// ã¢ãã¯çšã³ã³ã¹ãã©ã¯ã¿
public S3Service(S3Client s3Client) {
this.s3Client = s3Client;
}
// å®ç°å¢çšã³ã³ã¹ãã©ã¯ã¿
public S3Service(Region region) {
this(S3Client.builder().region(region).build());
}
}
7.2 @Mockãš@InjectMocksãé©åã«äœ¿çšãã
@Mock ã§ã¢ãã¯ãªããžã§ã¯ããäœæãã@InjectMocks ã§ã¢ãã¯ãªããžã§ã¯ããæ³šå ¥ããŸãã
@ExtendWith(MockitoExtension.class)
public class S3ServiceTest {
@Mock
private S3Client s3Client;
@InjectMocks
private S3Service s3Service;
}
7.3 whenãšverifyã§ã¡ãœããã®æåãšåŒã³åºããæ€èšŒãã
when ã§ã¢ãã¯ãªããžã§ã¯ãã®æ¯ãèããå®çŸ©ããverify ã§ã¡ãœãããæåŸ éãã«åŒã³åºãããã確èªããŸãã
@Test
void ãã¡ã€ã«ãããŠã³ããŒãã§ããããš() {
/* æºå */
when(s3Client.getObject(any(GetObjectRequest.class), any()))
.thenReturn(mockResponse);
/* å®è¡ */
s3Service.downloadFile("bucket", "key");
/* æ€èšŒ */
verify(s3Client).getObject(eq(expectedRequest), any());
}
7.4 äŸå€ãçºçããã±ãŒã¹ããã¹ããã
doThrow ã䜿çšããŠäŸå€ãã¹ããŒããããã«èšå®ããassertThrows ã§äŸå€ãçºçããããšã確èªããŸãã
@Test
void ã¢ããããŒãæã«äŸå€ãçºçããå Žåã®ãã¹ã() {
/* æºå */
doThrow(SdkClientException.class)
.when(s3Client).putObject(any(PutObjectRequest.class), any(RequestBody.class));
/* å®è¡ãšæ€èšŒ */
assertThrows(SdkClientException.class, () -> {
s3Service.uploadData("bucket", "key", "data".getBytes());
});
}
7.5 AWSãµãŒãã¹ã®å¶çŽã仿§ãæ£ããçè§£ãã
AWS SDK ã®ããã¥ã¡ã³ããçèªããå¶éã仿§ãææ¡ããŸããå®éã®ç°å¢ã§ã®åäœãšäžèŽããããã«ã¢ãã¯ã®æ¯ãèããèšå®ããŸãã
@Test
void DynamoDBã®ãããæžã蟌ã¿å¶éãèæ
®ãããã¹ã() {
/* æºå */
List<WriteRequest> items = generateItems(30); // 25ä»¶ãè¶
ããã¢ã€ãã
/* å®è¡ */
dynamoDbService.batchWriteItems("TestTable", items);
/* æ€èšŒ */
// 25ä»¶ãã€2åã«åããŠåŒã³åºãããããšã確èª
verify(dynamoDbClient, times(2)).batchWriteItem(any(BatchWriteItemRequest.class));
}
8. çšèªé
| çšèª | 説æ |
|---|---|
| AWS SDK | AWSãµãŒãã¹ãããã°ã©ã ããæäœããããã®ã©ã€ãã©ãª |
| ã¢ã㯠| æ¬æ¥ã®ãµãŒãã¹ããªããžã§ã¯ãã®åäœãæš¡å£ãããã¹ãçšã®ãããŒãªããžã§ã¯ã |
| Mockito | Javaã®ã¢ãã¯ãã¬ãŒã ã¯ãŒã¯ã§ããã¹ãæã«äŸåãªããžã§ã¯ããæš¡æ¬åããããã«äœ¿çšããããŒã« |
| Amazon DynamoDB | AWSãæäŸããNoSQLããŒã¿ããŒã¹ãµãŒãã¹ |
| Amazon S3 | AWSãæäŸãããªããžã§ã¯ãã¹ãã¬ãŒãžãµãŒãã¹ |
| ã³ã³ã¹ãã©ã¯ã¿ | ãªããžã§ã¯ãã®åæåæã«åŒã³åºãããã¡ãœãã |
| ã¢ãããŒã·ã§ã³ | ã³ãŒãã«ä»å æ å ±ãäžããèšè¿°ã§ãç¹å®ã®åäœãæå®ããããã«äœ¿çšãã |
| ãã¹ããã¬ãŒã ã¯ãŒã¯ | ãã¹ããå¹ççã«è¡ãããã®ããŒã«ãã©ã€ãã©ãªã®éå |
| ãšã©ãŒãã³ããªã³ã° | ãšã©ãŒãäŸå€ãçºçãããšãã®åŠçæ¹æ³ãå®çŸ©ããããš |
9. Appendix
DynamoDbServiceãS3Service ãå®éã®ãªãœãŒã¹ã«å¯ŸããŠæäœã§ããã確èªããããã®ãã¹ãã³ãŒããšããã¹ãã«å¿ èŠãªãªãœãŒã¹ãæ§ç¯ããããã®CloudFormation ãã³ãã¬ãŒããbuild.gradle ãèšèŒããŸãã
9.1 å®ãªãœãŒã¹ãæäœããã³ãŒãäŸ
import lombok.extern.slf4j.Slf4j;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
@Slf4j
public class Main {
public static void main(String[] args) {
// 䜿çšããAWSãªãŒãžã§ã³ãæå®
Region region = Region.AP_NORTHEAST_1; // æ±äº¬ãªãŒãžã§ã³ãªã©ãã奜ã¿ã®ãªãŒãžã§ã³ã«å€æŽããŠãã ãã
// DynamoDbServiceã®ã€ã³ã¹ã¿ã³ã¹ãäœæ
DynamoDbService dynamoDbService = new DynamoDbService(region);
// æäœããããŒãã«åãæå®
String tableName = "TestTable"; // äºåã«äœæããŠããå¿
èŠããããŸã
// DynamoDBã«ä¿åããã¢ã€ãã ãäœæ
Map<String, AttributeValue> item = new HashMap<>();
item.put("id", AttributeValue.builder().s("123").build());
item.put("name", AttributeValue.builder().s("ãã¹ããŠãŒã¶ãŒ").build());
item.put("email", AttributeValue.builder().s("test@example.com").build());
// ã¢ã€ãã ãä¿å
log.info("DynamoDBã«ã¢ã€ãã ãä¿åããŸã...");
dynamoDbService.putItem(tableName, item);
log.info("ã¢ã€ãã ãä¿åããŸããã");
// ã¢ã€ãã ãååŸããããã®ããŒãäœæ
Map<String, AttributeValue> key = new HashMap<>();
key.put("id", AttributeValue.builder().s("123").build());
// ã¢ã€ãã ãååŸ
log.info("DynamoDBããã¢ã€ãã ãååŸããŸã...");
Map<String, AttributeValue> retrievedItem = dynamoDbService.getItem(tableName, key);
log.info("ååŸããã¢ã€ãã : " + retrievedItem);
// S3Serviceã®ã€ã³ã¹ã¿ã³ã¹ãäœæ
S3Service s3Service = new S3Service(region);
// æäœãããã±ããåãšãªããžã§ã¯ãããŒãæå®
String bucketName = "test-tsuji-junit-sample-sdk"; // äºåã«äœæããŠããå¿
èŠããããŸã
String objectKey = "test.txt";
// ã¢ããããŒãããããŒã¿ãäœæ
String content = "ããã¯ãã¹ããã¡ã€ã«ã®å
容ã§ãã";
byte[] data = content.getBytes(StandardCharsets.UTF_8);
// ãã¡ã€ã«ãã¢ããããŒã
log.info("S3ã«ãã¡ã€ã«ãã¢ããããŒãããŸã...");
s3Service.uploadData(bucketName, objectKey, data);
log.info("ãã¡ã€ã«ãã¢ããããŒãããŸããã");
// ãã¡ã€ã«ãããŠã³ããŒã
log.info("S3ãããã¡ã€ã«ãããŠã³ããŒãããŸã...");
byte[] downloadedData = s3Service.downloadFile(bucketName, objectKey);
String downloadedContent = new String(downloadedData, StandardCharsets.UTF_8);
log.info("ããŠã³ããŒããããã¡ã€ã«ã®å
容: " + downloadedContent);
}
}
ãã°ã«ã¯logback ãå©çšããŠããã以äžã®ãããªèšå®ãšããŠããŸã
<configuration>
<property name="ROOT_LEVEL" value="INFO" />
<appender name="jsonConsoleAppender"
class="ch.qos.logback.core.ConsoleAppender">
<encoder
class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp>
<timeZone>Asia/Tokyo</timeZone>
</timestamp>
<pattern>
<pattern>
{
"Level": "%level",
"message": "%message%ex"
}
</pattern>
</pattern>
</providers>
</encoder>
</appender>
<root level="info">
<appender-ref ref="jsonConsoleAppender" />
</root>
</configuration>
9.3 CloudFormation ãã³ãã¬ãŒã
以äžã®ãã³ãã¬ãŒãã§ã¯ãDynamoDB TableãS3 ãã±ãããäœæããŸãã
AWSTemplateFormatVersion: '2010-09-09'
Description: >
Template to create a DynamoDB table and an S3 bucket for testing DynamoDbService and S3Service.
Parameters:
# Parameter to specify the S3 bucket name
BucketName:
Type: String
Description: >
The name of the S3 bucket to create.
- Only lowercase letters, numbers, dots (.), and hyphens (-) are allowed.
- Must be between 3 and 63 characters in length.
- The bucket name must be globally unique.
ConstraintDescription: S3 bucket name must be a valid format.
# Parameter to specify the DynamoDB table name
DynamoDBTableName:
Type: String
Description: >
The name of the DynamoDB table to create.
- Only alphanumeric characters (both uppercase and lowercase), underscores (_), hyphens (-), and dots (.) are allowed.
- Must be between 3 and 255 characters in length.
- Default value is "TestTable".
Default: TestTable
ConstraintDescription: DynamoDB table name must be a valid format.
Resources:
# Creation of the DynamoDB table
TestDynamoDBTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Ref DynamoDBTableName
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
BillingMode: PAY_PER_REQUEST # On-Demand Capacity Mode
# Creation of the S3 bucket
TestS3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref BucketName
AccessControl: Private
# Additional properties like BucketPolicy can be added here if needed
Outputs:
# Output the DynamoDB table name
DynamoDBTableNameOutput:
Description: The name of the created DynamoDB table
Value: !Ref TestDynamoDBTable
# Output the S3 bucket name
S3BucketNameOutput:
Description: The name of the created S3 bucket
Value: !Ref TestS3Bucket
9.3 build.gradle
å®ãªãœãŒã¹æäœæçšã®logback ã®èšå®çãå«ã¿ãŸãã
plugins {
id 'java'
}
version = '1.0-SNAPSHOT'
java {
sourceCompatibility = '17'
}
repositories {
mavenCentral()
}
compileJava.options.encoding = 'UTF-8'
compileTestJava.options.encoding = 'UTF-8'
dependencies {
// AWS SDK for DynamoDB
implementation 'software.amazon.awssdk:dynamodb:2.28.1'
// AWS SDK for S3
implementation 'software.amazon.awssdk:s3:2.28.1'
// JUnit 5
testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0'
// Mockito
testImplementation 'org.mockito:mockito-core:5.13.0'
testImplementation 'org.mockito:mockito-junit-jupiter:5.13.0'
// logger
implementation 'ch.qos.logback:logback-classic:1.5.8'
implementation 'net.logstash.logback:logstash-logback-encoder:8.0'
compileOnly 'org.projectlombok:lombok:1.18.34'
annotationProcessor 'org.projectlombok:lombok:1.18.34'
}
test {
useJUnitPlatform()
}
tasks.withType(JavaCompile).configureEach {
options.encoding = 'UTF-8'
}