AWS Lambdaのための関数のローカル開発とテスト


これは個人の意見/メモであり、所属する組織を代表するものではありません。

## サマリ

[AWS Lambda](http://aws.amazon.com/lambda/)のLambda Funcitonをローカルで開発する環境について考えてみた。

なお、このポストの続きを[AWS Lambdaの関数をnpmでパッケージ管理](http://qiita.com/imaifactory/items/ac81b4a3ff4a5f5dec85)に書いています。

## 大まかな方法

Lambdaでは、例えば以下のような感じで関数を実装してアップロードする。

```myFirstLambda.js

var AWS = require("aws-sdk");
var DOC = require("dynamodb-doc");

AWS.config.update({region: "us-west-2"});

exports.handler = function(event, context){
    this.docClient = new DOC.DynamoDB();
    
    //Do something with DynamoDB
    
    context.done();
}

```

で、実行の際にはmyFirstLambda.jsがrequireされてhandlerメソッド(メソッド名はLambdaの設定で変更可能)が呼ばれる、という非常に単純な話（少なくとも外から見る限り）なので、以下のようなドライバスクリプトを書いてあげればOK。

```driver.js

//YOUR DATA TO BE PASSED TO LAMBDA FUNCTION.
var event = {
  "Records": [
    {
      "awsRegion": "us-west-2",
      "sequenceNumber": "196800000000000000000374",
      "partitionKey": "2efdb0ea22685b46993e42a67302a001",
      "eventSource": "aws:kinesis",
      "data": "SOME CUSTOM DATA 1"
    }
  ]
};

//BUILD STAB OF context OBJECT.
var context = {
    invokeid: 'invokeid',
    done: function(err,message){
        return;
    }
};


//RUN YOUR HANDLER
var lambda = require("../myFirstLambda");
lambda.handler(event,context);
```

### handlerに渡ってくるeventとcontextというオブジェクトについての補足

- **event**: 実際に渡ってくるイベントのデータ(S3のアップデートとかKinesisのレコードとか）。
- **context**: Lambda functionの呼び出しコンテキスト。done()というAPIを呼び出すと関数が終了するのだが、コードをちょっと追いきれてないのでとりあえず単純にreturnするだけな感じにstub化。ちなみにcontextオブジェクトのダンプは以下のとおり。done()の中で呼び出されているpostDone()は後で追ってみる。

```context

{
	invokeid: 'STRING', // Lambda functionの呼び出しIDのようなもの（恐らく呼び出しごとにユニーク）
	done: function (err, message) {
        if(doneStatus) {
            return;
        }
        doneStatus = true;
        var error = null;
        if(!(typeof err == "undefined" || (typeof err == "object" && !err))) {
            error = util.format(err);
            console.log(error);
        }
        /*
        * use a timeout to perform the operation once the user gives up control of the event thread
        * This is how HTTP handler works right now
        */
        setTimeout(function() {
            postDone(error, message);
       }, 0);
    }
}

```

というわけで、こんな感じにドライバスクリプトを準備してあげれば、コード書いてLambdaにアップしてSave & Invokeして・・・みたいなのを繰り返さずにもローカルで開発ができるよという話でした。

## テストを書く

ドライバが書けたので、ちょっと手を加えればテストも書ける。

```test/basic.js
var assert = require('assert');

//YOUR_EVENT_OBJECT
var data = {};

var context = {
    invokeid: 'invokeid',
    done: function(err,message){
        return;
    }
};

describe('myFirstLambda',function(){
    it('Should have docClient', function(){
        var lambda = require("../myFirstLambda");
        lambda.handler(data,context);
        assert(lambda.docClient);
    });
});

```

で、package.jsonにこんな感じにテスト実行と依存関係について定義しておけばLambda用の関数をnpmパッケージとして　管理できる・・・気がする。

```package.json

{
  "name": "myFirstLambda",
  "version": "0.0.1",
  "private": true,
  "dependencies": {
    "aws-sdk":"2.*",
    "dynamodb-doc":"1.*"
  },
  "devDependencies": {
    "mocha": "*",
    "eslint": "*",
    "istanbul": "*"
  },
  "scripts" : {
    "test" : "npm -s run-script lint && npm -s run-script unit",
    "unit" : "istanbul `[ $COVERAGE ] && echo 'cover _mocha' || echo 'test mocha'` -- test test/basic",
    "lint" : "eslint ./*.js"
  }
}

```

あとはscriptに

- build: 必要なファイルをzipしてpkg/配下に吐き出す
- deploy: AWS Lamdaにアップロードする

あたりを付け足してあげるとよりパッケージ管理が楽になりそうなんだけど、それはまた今度。

## 追記

続き書いた: [AWS Lambdaの関数をnpmでパッケージ管理](http://qiita.com/imaifactory/items/ac81b4a3ff4a5f5dec85)