Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

AWS Velocity Series: Local Development Environment

DZone's Guide to

AWS Velocity Series: Local Development Environment

The local development environment is where you make changes to the source code, edit configuration files, and add images. It's key to AWS velocity.

· DevOps Zone
Free Resource

The DevOps Zone is brought to you in partnership with Sonatype Nexus. The Nexus Suite helps scale your DevOps delivery with continuous component intelligence integrated into development tools, including Eclipse, IntelliJ, Jenkins, Bamboo, SonarQube and more. Schedule a demo today

Most of our clients use AWS to reduce time-to-market following an Agile approach. However, AWS is only one part of the solution. In this article series, I show you how we help our clients to improve velocity: the time from idea to production.

Local Development Environment

The local development environment is where you make changes to the source code, edit configuration files, add images, and so on. You also want to run the app locally, execute the tests, and be able to debug your source code.

AWS Velocity: Local development environment

In this article, you will learn how to setup a Node.js project from scratch with:

  • Unit tests to cover internal functionality.
  • Code checks to get rid of typos and code smells early.
  • Acceptance tests to verify the end-user behavior.

The Node.js app will offer an HTTP API to compute factorials. The concepts can be applied to any other programming language. Let’s get started!

Set Up Project

Many folders and files are needed. You can follow step-by-step or get the full source code here.

First, create a folder for the new project. I assume that you use macOS or Linux running a terminal:

mkdir aws-velocity
cd aws-velocity/

Within the new project folder, create folders for your app, infrastructure, deploy scripts, and your acceptance tests.

mkdir app
mkdir infrastructure
mkdir deploy
mkdir acceptance

The project structure should look like this now:

tree ../aws-velocity/
#../aws-velocity/
#├── acceptance
#├── app
#├── deploy
#└── infrastructure
#4 directories, 0 files

Let’s develop the app next.

A Simple App

Now, you need a simple app that you can use as an example. The app will be based on Node.js but the concepts will apply to all other programming languages. If you don’t have Node.js installed, visit nodejs.org.

cd app/
mkdir test
mkdir lib

Now, create the app structure with the npm init tool. The tool will ask you a bunch of questions, make sure to answer them as I did:

npm init
# name: (app) factorial
# version: (1.0.0) 1.0.0
# description: Factorial as a Service
# entry point: (index.js) index.js
# test command: ./node_modules/.bin/jshint . && ./node_modules/.bin/mocha test/*.js
# git repository: 
# keywords: 
# author: 
# license: (ISC) 
# About to write to /Users/michael/Desktop/aws-velocity/app/package.json:
# {
#   "name": "factorial",
#   "version": "1.0.0",
#   "description": "Factorial as a Service",
#   "main": "index.js",
#   "directories": {
#     "test": "test"
#   },
#   "scripts": {
#     "test": "jshint . && ./node_modules/.bin/mocha test/*.js"
#   },
#   "author": "",
#   "license": "ISC"
# }
# Is this ok? (yes) yes

You also need to install a few dependencies. express is a popular web framework, jshint is a code quality tool and mocha is a test framework:

npm install express@4.14.0 --save
npm install jshint@2.9.4 --save-dev
npm install mocha@3.2.0 --save-dev

jshint needs a little bit of configuration. Create a file .jshintrc with the following content:

{
  "esversion": 5,
  "node": true
}

Create a file test/.jshintrc with the following content:

{
  "extends": "../.jshintrc",
  "mocha": true
}

Create a file .jshintignore with the following content:

node_modules/**

Now the app structure is done. Let’s see if the test command works:

npm test
# > factorial@1.0.0 test /Users/michael/Desktop/aws-velocity/app
# > jshint . && ./node_modules/.bin/mocha test/*.js
# 
# Warning: Could not find any test files matching pattern: test/*.js
# No test files found
# npm ERR! Test failed.  See above for more details.

The tests fail because there are no tests. So let’s add some unit tests for the factorial implementation.

Create a file test/factorial.js with the following content. The structure of the file is determined by the test framework mocha. The important lines start with assert.equal. Those lines contain actual test conditions that the implementation must satisfy:

'use strict';

var factorial = require('../lib/factorial.js');
var assert = require('assert');

describe('factorial', function() {
    it('should fail for < 0', function() {
        assert.throws(function() {
            factorial(-1);
        });
    });
    it('should return 1 for 0', function() {
        assert.equal(factorial(0), 1);
    });
    it('should return 1 for 1', function() {
        assert.equal(factorial(1), 1);
    });
    it('should return 2 for 2', function() {
        assert.equal(factorial(2), 2);
    });
    it('should return 6 for 3', function() {
        assert.equal(factorial(3), 6);
    });
    it('should return 24 for 4', function() {
        assert.equal(factorial(4), 24);
    });
    it('should return 120 for 5', function() {
        assert.equal(factorial(5), 120);
    });
    it('should return 87178291200 for 14', function() {
        assert.equal(factorial(14), 87178291200);
    });
    it('should fail for > 14', function() {
        assert.throws(function() {
            factorial(15);
        });
    });
});

If you run npm test again, the test still fails because there is no implementation yet. Let’s change that.

Create a file lib/factorial.js with the following content. The factorial implementation is recursive:

'use strict';

function factorial(n) {
    if (n === 0) {
        return 1;
    } else {
        return n * factorial(n - 1);
    }
}

module.exports = factorial;

Let’s see if the tests pass now by running npm test.

Oh, no! The implementation is missing some checks for edge cases. Change the file lib/factorial.js accordingly:

'use strict';

function factorial(n) {
    if (n < 0) {
        throw new Error('not defined for negative numbers');
    }
    if (n > 14) {
        throw new Error('not implemented for large numbers');
    }
    if (n === 0) {
        return 1;
    } else {
        return n * factorial(n - 1);
    }
}

module.exports = factorial;

Now, wire expresses up to offer an HTTP endpoint for factorial computation. Create a file index.js with the following content:

'use strict';

var factorial = require('./lib/factorial.js');
var express = require('express');
var app = express();

app.get('/:n', function(req, res) {
    var n = parseInt(req.params.n, 10);
    if (n < 0 || n > 14) {
        res.sendStatus(400);
    } else {
        res.send(factorial(n).toString());
    }
});

var port = process.env.PORT || 3000;
app.listen(port, function() {
    console.log('app listening on port ' + port);
});

You can now start the app locally:

node index.js
# app listening on port 3000

In another terminal, execute curl to make an HTTP request against your app:

curl http://localhost:3000/5
# 120

So far, so good. It’s time to add an acceptance test.

cd ..
cd acceptance/

Now, create the acceptance test structure. The acceptance test is an independent application also using Node.js. The npm init command will setup the project. Make sure to configure it like I did:

npm init
# name: (acceptance) factorial-acceptance
# version: (1.0.0) 1.0.0
# description: Acceptance test for Factorial as a Service
# entry point: (spec.js) 
# test command: ./node_modules/.bin/jshint .
# git repository: 
# keywords: 
# author: 
# license: (ISC) 
# About to write to /Users/michael/Desktop/aws-velocity/acceptance/package.json:
# {
#   "name": "acceptance",
#   "version": "1.0.0",
#   "description": "",
#   "main": "spec.js",
#   "scripts": {
#     "test": "jshint ."
#   },
#   "author": "",
#   "license": "ISC"
# }
#Is this ok? (yes) yes

Again, you need to install a few dependencies. frisby helps us to test REST APIs, jasmine is yet another test framework, and jshint will ensure code quality:

npm install frisby @0 .8 .5--save
npm install jasmine - node @1 .14 .5--save
npm install jshint @2 .9 .4--save - dev

jshint needs some configuration. Create a file .jshintrc with the following content:

{
    "esversion": 5,
    "node": true,
    "jasmine": true
}

Create a file .jshintignore with the following content:

node_modules/**

Now the app structure is created. Let’s see if the test command works:

npm test

Everything is okay. Now you need to implement the acceptance test.

Create a file factorial_spec.js (the file must end with _spec.js!) with the following content. The structure is determined by frisby, lines with a condition start with .expect:

'use strict';

var frisby = require('frisby');

if (process.env.ENDPOINT === undefined) {
    throw new Error('ENDPOINT environment variable missing');
}

frisby.create('/-1')
    .get(process.env.ENDPOINT + '/-1')
    .expectStatus(400)
    .toss();

frisby.create('/0')
    .get(process.env.ENDPOINT + '/0')
    .expectStatus(200)
    .expectBodyContains('1')
    .toss();

frisby.create('/14')
    .get(process.env.ENDPOINT + '/14')
    .expectStatus(200)
    .expectBodyContains('87178291200')
    .toss();

frisby.create('/15')
    .get(process.env.ENDPOINT + '/15')
    .expectStatus(400)
    .toss();

To execute the acceptance tests against your locally running app (if you stopped the app, run node index.js in a separate terminal), run:

ENDPOINT = "http://localhost:3000". / node_modules / .bin / jasmine - node.
#Finished in 0.083 seconds
#4 tests, 6 assertions, 0 failures, 0 skipped

The application is done. You have unit tests, and you also have acceptance tests. The important differences between the two are:

  • Unit tests can run without spawning a web server.
  • Unit tests ensure that the factorial function returns the correct values.
  • Acceptance tests ensure that the REST API works as expected (i.e., as documented, or as it behaved before).

In the next article, you will learn how to setup the CI/CD pipeline for your new app.

The DevOps Zone is brought to you in partnership with Sonatype Nexus. Use the Nexus Suite to automate your software supply chain and ensure you're using the highest quality open source components at every step of the development lifecycle. Get Nexus today

Topics:
javascript ,aws ,devops ,velocity ,developer environment

Published at DZone with permission of Michael Wittig, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}