Skip to content

Commit c3ff812

Browse files
authored
Merge pull request #7 from irvinlim/test-runner
Add test runner
2 parents 989fd5b + 710750b commit c3ff812

File tree

8 files changed

+208
-37
lines changed

8 files changed

+208
-37
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ before_install:
88
- docker pull lambci/lambda
99
script:
1010
- npm run spec
11+
- npm run test

README.md

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
[![Travis CI](https://img.shields.io/travis/irvinlim/es2017-lambda-boilerplate.svg?style=flat-square)](https://travis-ci.org/irvinlim/es2017-lambda-boilerplate) [![GitHub](https://img.shields.io/github/release/irvinlim/es2017-lambda-boilerplate.svg?style=flat-square)](https://github.com/irvinlim/es2017-lambda-boilerplate/releases) [![The MIT License](https://img.shields.io/badge/license-MIT-orange.svg?style=flat-square)](http://opensource.org/licenses/MIT)
44

5-
## What is it?
6-
75
This is a boilerplate for [AWS Lambda](https://aws.amazon.com/lambda/) Node.js 6.10.0 functions, which allows you to use the latest JavaScript [ES2017/ES8 features](https://hackernoon.com/es8-was-released-and-here-are-its-main-new-features-ee9c394adf66) within a Lambda function.
86

97
This boilerplate adds support for the following most commonly used JavaScript features that are not natively supported on AWS Lambda:
@@ -24,21 +22,25 @@ _Note: Only features which are not normally available on AWS Lambda Node.js 6.10
2422

2523
## Usage
2624

27-
Edit your Lambda function under `src/index.js`, and run:
25+
Edit your Lambda function under `src/main.js`, and run:
2826

29-
```
27+
```js
3028
npm run package
3129
```
3230

3331
This will create an `artifact.zip` file which you can upload to AWS Lambda.
3432

3533
## Testing
3634

37-
### Test boilerplate
35+
You can run automated tests for your Lambda function inside of a Docker container using [docker-lambda](https://github.com/lambci/docker-lambda):
3836

39-
_NOTE: Test boilerplate under development._
37+
```js
38+
npm run test
39+
```
40+
41+
The test runner used is [Jest](https://github.com/facebook/jest) (with [Jasmine](https://jasmine.github.io)). All files in the `test/` directory which end with `.test.js` will be interpreted as a test suite.
4042

41-
The boilerplate allows you to run automated tests using [docker-lambda](https://github.com/lambci/docker-lambda)
43+
This also requires Docker to be installed on your host; see the [docs for docker-lambda](https://github.com/lambci/docker-lambda) for more instructions.
4244

4345
### Specification tests
4446

@@ -47,15 +49,17 @@ In order to ensure that the Babel configuration works and is following the spec,
4749
* **Functional testing**: Runs the relevant spec tests from [Test262](https://github.com/tc39/test262) (actual tests taken from [node.green](http://node.green/)) on [docker-lambda](https://github.com/lambci/docker-lambda) to mock the AWS Lambda environment
4850
* **Snapshot testing**: Unit testing strategy by storing snapshots of Babel-transformed source code and running unit tests against them
4951

52+
You can find the spec tests under `spec/functional` and `spec/snapshot` respectively.
53+
54+
If you are not going to modify `.babelrc`, you can choose to skip these tests by omitting the `npm run spec` script in `.travis.yml`. This will help to speed up your builds by a bit.
55+
5056
## Why?
5157

5258
### Latest ES2017 features
5359

5460
Even though Lambda supposedly supports Node.js 6.10.0, not all JavaScript features are supported. [www.whatdoeslambdasupport.com](http://www.whatdoeslambdasupport.com/) has a comprehensive list of what is supported and what are not.
5561

56-
If you are used to using features like `async`/`await` which have been around for a while now, you might find it tedious to build your own tooling to transpile all the latest ECMAScript features that you have been using all along.
57-
58-
That's why I built this boilerplate - using `async`/`await` was really important to me when making use of the AWS SDK on Lambda, like as follows:
62+
This boilerplate adds support for the most commonly used features that are not available on Node 6.10.0 or AWS Lambda, such as `async`/`await` when used with the [AWS SDK](https://github.com/aws/aws-sdk-js):
5963

6064
```js
6165
const EC2 = new AWS.EC2();
@@ -73,21 +77,41 @@ await Route53.changeResourceRecordSets({
7377
}).promise();
7478
```
7579

76-
The usage of `async`/`await` reduces all the cruft involved when using either normal AWS SDK callbacks or chaining Promises.
77-
78-
### Internet connectivity handling
80+
### Run automated tests locally/through CI
7981

80-
I was also bitten badly by the fact that placing a Lambda function in a VPC requires a NAT gateway in order for the Lambda function to have outbound Internet connectivity, and I was trying to use the bundled AWS SDK to perform operations on the AWS API.
82+
Instead of testing your Lambda function by uploading to AWS Lambda every single time, running automated tests in conjunction with CI is a better option. By using Docker to mock the AWS Lambda environment locally, you can write test cases to verify the correctness of your function, given an input (the [Lambda event](http://docs.aws.amazon.com/lambda/latest/dg/eventsources.html)):
8183

82-
Not knowing that using the SDK requires Internet connectivity (I assumed that the SDK could call the IPv4 link-local address for the metadata server `http://169.254.169.254` for API calls, and thus required for it to be placed in a VPC), I was stuck for a good couple of hours to find out why my Lambda functions consistently hit the 30s timeout I had set.
83-
84-
This boilerplate performs a quick Internet connectivity test (up to 1000ms) to help you guard and debug against this problem, terminating the execution instead of timing out only after the full duration of the Lambda execution time.
84+
```js
85+
import run from './util/runner';
86+
87+
it('should work', function() {
88+
// Sample event from SNS.
89+
const event = {
90+
Records: [
91+
{
92+
EventVersion: '1.0',
93+
EventSource: 'aws:sns',
94+
Sns: {
95+
MessageId: '95df01b4-ee98-5cb9-9903-4c221d41eb5e',
96+
Message: 'Hello from SNS!',
97+
...
98+
},
99+
},
100+
],
101+
};
102+
103+
// Run the Lambda function against this event.
104+
const result = run(event);
105+
106+
expect(result).toEqual(true);
107+
});
108+
```
85109

86-
Set the `INTERNET_CONNECTIVITY_TEST` constant to `true` in order to use this feature, otherwise it will not invoke the Internet connectivity test.
110+
This strategy also does not utilise your AWS Lambda invocation credits - meaning you are free to run as many tests as often as you like!
87111

88112
## Acknowledgements
89113

90-
This boilerplate was inspired from this [post](http://jessesnet.com/development-notes/2016/nodejs-es7-aws-lambda/) by Jesse Cascio.
114+
This boilerplate was first inspired from this [post](http://jessesnet.com/development-notes/2016/nodejs-es7-aws-lambda/) by Jesse Cascio.
91115

92116
## License
93117

package-lock.json

Lines changed: 88 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,15 @@
3333
"package:pack": "rm -f artifact.zip && cd dist/ && zip -r ../artifact.zip *",
3434
"spec": "npm run spec:snapshot && npm run spec:functional",
3535
"spec:snapshot": "jest spec/snapshot",
36-
"spec:functional": "jest spec/functional"
36+
"spec:functional": "jest spec/functional",
37+
"test": "npm run test:build && npm run test:run",
38+
"test:build": "npm run test:build:init && npm run test:build:js && npm run test:build:install",
39+
"test:build:init": "cd test && npm run build:init",
40+
"test:build:js": "cd src && babel . -d ../test/dist",
41+
"test:build:install": "cp package.json test/dist/ && cd test/dist && npm install --production",
42+
"test:run": "jest test/"
3743
},
38-
"dependencies": {}
44+
"dependencies": {
45+
"jsonrequest": "^4.2.3"
46+
}
3947
}

src/index.js

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,14 @@
33
* The following is written using ES2017/ES8, and should be transpiled to ES6 before uploading to AWS Lambda.
44
*/
55

6-
// The SDK is automatically available on Lambda and does not need to be uploaded through node_modules.
7-
const AWS = require('aws-sdk');
8-
9-
// Utility function to test for Internet connectivity.
10-
const internetConnectivityTest = require('./util/internetConnectivityTest');
6+
import internetConnectivityTest from './util/internetConnectivityTest';
7+
import lambdaMain from './main';
118

129
// Helps to guard against timeouts when Lambda is placed in a VPC without a NAT gateway,
1310
// which means that it does not have an outbound Internet connection.
1411
// Set to 'false' if your Lambda function does not need use the SDK methods or require Internet connectivity.
1512
const INTERNET_CONNECTIVITY_TEST = true;
1613

17-
/**
18-
* Main method for Lambda goes here. async/await works!
19-
*/
20-
const lambdaMain = async (event, context) => {
21-
// Example SDK call.
22-
return await new AWS.EC2().describeInstances().promise();
23-
};
24-
2514
/**
2615
* Wrapper to pass errors or return results to callback().
2716
*/

src/main.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Feel free to modify your Lambda function here!
3+
*/
4+
5+
import request from 'jsonrequest';
6+
7+
const lambda = async (event, context) => {
8+
const { user, repo } = event;
9+
10+
// Make a REST API call to the GitHub API.
11+
const options = {
12+
url: `https://api.github.com/repos/${user}/${repo}`,
13+
headers: { 'User-Agent': 'AWS Lambda' },
14+
};
15+
16+
// Await the Promise.
17+
return await request(options);
18+
};
19+
20+
module.exports = lambda;

test/example.test.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* Example test using Jest/Jasmine.
3+
*
4+
* Use run() to run your Lambda function within docker-lambda.
5+
*/
6+
7+
import run from './util/runner';
8+
9+
it('should fetch a valid repo from GitHub API successfully', function() {
10+
const event = {
11+
user: 'irvinlim',
12+
repo: 'es2017-lambda-boilerplate',
13+
};
14+
15+
const result = run(event);
16+
17+
expect(result.owner.login).toEqual('irvinlim');
18+
});

0 commit comments

Comments
 (0)