概要
前回はS3をローカルで立ち上げた。
今回は、ローカル環境でLambda+S3のテストをするを参考に、リファクタリングをしてテストを行う。
構造
src
- lambda
- handlers
- s3-sample.ts
- service
- s3-sample.ts
test
- fixture
- s3
- message.txt
- lambda
- service
- s3-sample.test.ts
ソース
- 処理をgetS3Objectに移し、インスタンスを作って渡すようにする。
src/lambda/hendler/s3-sample.ts
import { S3 } from 'aws-sdk';
import { Handler } from 'aws-lambda';
import { getS3Object } from '../service/s3-sample';
const s3 = new S3();
const { S3_BUCKET } = process.env;
if (!S3_BUCKET) throw Error('env S3_BUCKET is empy')
export const lambdaHandler: Handler = async (event, context, callback) => {
try {
const message = getS3Object({ s3, key: event.Key, bucket: S3_BUCKET, callback });
console.log(message)
} catch (err) {
console.log(err);
return err;
}
};
- getS3Objectでは、使用するインスタンスは外部から受け取る。DI。
src/lambda/service/s3-sample.ts
import type { S3 } from 'aws-sdk';
import { Callback } from 'aws-lambda';
interface Payload {
s3: S3
key: string
bucket: string
callback: Callback
}
export const getS3Object: (o: Payload) => Promise<string> = async ({ s3, bucket, key, callback }) => {
const params = {
Bucket: bucket,
Key: key
};
const ret = await s3.getObject(params).promise();
if (!ret.Body) throw Error(`s3 object is empty. Bucket: ${bucket}, Key: ${key}`)
const message = ret.Body.toString();
console.log(message);
callback(null, `access ${key}`)
return message;
}
- テストではs3オブジェクトを作成して渡してやる。
test/lambda/service/s3-sample.test.ts
import * as fs from 'fs';
import * as path from 'path';
import { S3 } from 'aws-sdk';
import { getS3Object } from '@/lambda/service/s3-sample';
// localstack
const config = {
endpoint: 'http://localhost:4566',
s3ForcePathStyle: true,
};
const s3 = new S3(config);
/*
* test suits
*/
describe('core/getS3Object test suit #1', () => {
// shared variables over the test suit
let bucket: string;
let key: string;
const callback = () => { }
/*
* starup
*/
beforeEach(async () => {
// generate test resource sequence code from timestamp
const now = new Date().getTime();
bucket = `test-bucket-${now}`;
key = `message_${now}.txt`;
// create bucket & bucket key
await s3.createBucket({ Bucket: bucket }).promise();
await s3.putObject({
Bucket: bucket,
Key: key,
ContentType: 'text/plain',
Body: fs.readFileSync(path.join(__dirname, '..', '..', 'fixture', 's3', 'message.txt')),
}).promise();
});
/*
* test cases
*/
it('successfully get an object from S3 bucket', async () => {
const message = await getS3Object({ s3, bucket, key, callback });
expect(message).toBe('Hi, there.\n');
});
it('throw error if access to non-existent key', async () => {
try {
await getS3Object({ s3, bucket, key: 'non-existent', callback })
fail()
} catch (error) {
expect(error.name).toBe('NoSuchKey');
expect(error.message).toBe('The specified key does not exist.');
}
});
it('throw error if access to non-existent bucket', async () => {
try {
await getS3Object({ s3, bucket: 'non-existent', key: 'non-existent', callback })
fail()
} catch (error) {
expect(error.name).toBe('NoSuchBucket');
expect(error.message).toBe('The specified bucket does not exist');
}
});
/*
* tear-down
*/
afterEach(async () => {
// delete bucket key & bucket
await s3.deleteObject({
Bucket: bucket,
Key: key,
}).promise();
await s3.deleteBucket({ Bucket: bucket }).promise();
});
});
参考
Testable - lambda : 和田卓人
ローカル環境でLambda+S3のテストをする
github aws-sam-localstack-example
DI・DIコンテナ、ちゃんと理解出来てる・・?