AWS SDKを使ってもAWS環境にアクセスせずにユニットテストを回す方法

## 概要
aws-sdkを使ったライブラリなどのユニットテストを行うには特定のAWSアカウントが必要になります。AWSのアカウントを立ち上げてユニットテストをしてもいいんですが、どうしてもAWSアカウントに依存したり、ユニットテストの度にお金がかかります。

Travisでテストを回した時に、プルリクの度にテストが回ってお金がかかったり、IAMの設定がミスってて余計なAWSリソースを起動させられてしまったりとか、色々と考えないといけないことや制限が多くなります。

LambdaでCI環境を作る時も同じ悩みがつきものですね。
それを解決してくれるaws-sdk-mockというライブラリを見つけたので紹介します

## aws-sdk-mockとは
<img width="745" alt="スクリーンショット 2016-07-12 22.54.02.png" src="https://qiita-image-store.s3.amazonaws.com/0/65478/454a4dea-80c5-d58f-0490-57511d0e7c00.png">
<a href="https://github.com/dwyl/aws-sdk-mock">aws-sdk-mock</a>とはaws-sdkのモックを簡単に作ってくれるライブラリです。
aws-sdkで定義されているメソッドのモックを定義してくれます。

今回はモックに関する細かい説明は省きますが、簡単にいえば、AWS環境にアクセスしなくても擬似的にaws-sdkの各メソッドの返り値を返してくれます。要はaws-sdkを使っていてもAWS環境にアクセスすること無くユニットテストが実施できるんです。

## テスト対象のソースコード
今回は以下のClassを作ってみました。DynamoDBにputItem,getItem,deleteItemするメッソドが定義されてるClassです。

```js:users.js
'use strict'

var aws = require('aws-sdk')
    aws.config.update({region:'ap-northeast-1'})

class Users {

  constructor() {
    this.table = 'Users'
    this.dynamodb = new aws.DynamoDB()
  }

  putData(email) {
    let params = {
      TableName: this.table,
      Item     : { 'email': {'S':email} }
    }
   
    return this.dynamodb.putItem(params).promise()
  }

  getData(email) {
    let params = {
      TableName: this.table,
      Key      : { 'email': {'S':email} }
    }

    return this.dynamodb.getItem(params).promise()
  }
  
  deleteData(email) {
    let params = {
      TableName: this.table,
      Key      : { 'email': {'S':email} }
    }

    return this.dynamodb.deleteItem(params).promise()
  }
}
module.exports = Users
```

以下のように直接実行するとちゃんとDynamoDBにレコードができています。

```js
var user = new Users()
user.putData('panpanpan@example.com')
```
<img width="300" alt="スクリーンショット 2016-07-12 23.07.04.png" src="https://qiita-image-store.s3.amazonaws.com/0/65478/3e9b58ad-fa5e-8246-353c-5baa61237d2b.png">

## テストコード
では次にモックを定義してテストコードを作ります。以下がテストコードのすべてです。
3つのメソッドの中で使っているaws-sdkに対してモックを定義しています。

```js:users-spec.js
'use strict'

var aws    = require('aws-sdk-mock'),
    users  = require('../index'),
    chai   = require('chai'),
    path   = require('path'),
    should = chai.should(),
    input  = 'horike37@gmail.com',
    usersObj
    
aws.setSDK(path.resolve('node_modules/aws-sdk'))

describe('All Tests', function() {

  this.timeout(0)
  beforeEach(function() {
    aws.mock('DynamoDB', 'putItem', function(params, callback) {
      callback(null, 'successfully put item in database')
    })
    
    aws.mock('DynamoDB', 'getItem', function(params, callback) {
      callback(null, { Item: { email: params.Key.email.S } })
    })
    
    aws.mock('DynamoDB', 'deleteItem', function(params, callback) {
      callback(null, 'successfully delete item in database')
    })
    
    usersObj = new users()
  })
  

  it('putData', function(done) { 
    usersObj.putData(input).then(function(res) {
      res.should.equal('successfully put item in database')
      done()
    })
  })
  
  it('getData', function(done) {
    usersObj.getData(input).then(function(res) {
      res.Item.email.should.equal(input)
      done()
    })
  })
  
  it('deleteData', function(done) {
    usersObj.deleteData(input).then(function(res) {
      res.should.equal('successfully delete item in database')
      done()
    })
  })
})
```

DymanodbのputItemに対しては、問答無用で`successfully put item in database`が返るようになっています

```js
aws.mock('DynamoDB', 'putItem', function(params, callback) {
      callback(null, 'successfully put item in database')
    })
```

## テストコードの実行
ちゃんとDynamoDBのUsersテーブルは消した状態で以下のようにテストを走らせると正しく実行されました。これでLambdaでもAWSリソースを使う前提のコードはテストをしていけそうですね！

<img width="443" alt="スクリーンショット 2016-07-12 23.14.27.png" src="https://qiita-image-store.s3.amazonaws.com/0/65478/21cb59ea-7f20-0cf6-99c7-964196863d5a.png">