Create an online IDE with Angular 6 + NodeJS ~ Part #4

Ofir G
8 min readNov 12, 2018

--

Part #4 — Set up test suites for your server

What you will do on this part

On this part of the project you will integrate a logger service into your node app, to keep a better track on the data follow going through your server, and ease up a bit the debugging process.
Mainly, you will be instruct on how to configure test environment to your app build tests suites to your app, and test the API route on different cases with Node.JS best testing frameworks.

Contents Table — Part #4

  • Integrating Logger service
  • Build Up Test Suits
    ◢ Test-Environment configuration
    ◢ Test Suite Structure
    ◢ Start Testing
    ◢ API Route Testing

If you haven’t checked out the previous posts in the series, here are the links:
Part #1 — Building your IDE client-side with Angular and Bootstrap
Part #2 — Creating your server-side with Node.JS, Express and Typescript
Part #3 — Buildup your API’s routes

Integrating Logger service

Using a logger service instead of just spreading console.log around your code is a much better practice, only some of its benefits are :

  • It gives you control over the visual presentation of the content being logged (length limit, format, style/color, etc.).
  • You can manipulate the stream for the logged content be directed too (log-level stream, log file, etc.).
  • You can easily specify the conditions or the environments for the logger stream to be silenced.
  • The log level’s name, style, behavior, and stream are all completely in your control.

There are many great logging libraries out there; Morgan, Winston, Bunyan, Loglevel, and many more you could find on NPM with the search word “logger”.
Without any questions, you should always prefer using a library that has been tested and proved working by millions, over develop one on your own from scratch (unless that what your project about).

Because of my own visual preferences and some technical requirements, I’ve written my own (relatively simple) logger service and used it in most of my project.
So, before I could suggest you to use my logger service, please take a look 👇

How to log with Morgan :

How to log with Winston :

How to log with Bunyan :

My Simple Logger Service :

Here is a light version of my Simple Logger, when I say “light version”, I mean that it’s a little piece of the “Simple Logger” project source code, a piece that satisfies the requirements of this projects, with default config values designed specifically for this project use cases (not to exhaust you).
The source code of it here.
For the complete “Simple Logger” source code (with multiple log stream, ExpressJS configuration, filtering logs and more features) check my Git.

If you chose to use it (the light version), there are only 4 files of code, create them with the same content on the gist, place them all in the same folder (util/logger will be a great fit), and install colors package :

npm i colors --save

In your code, import sample-logger class and create an instance of it, now anywhere you previously used console.log now use one of the log levels methods in sample-logger class.

console.log('hello');    ==> 
loggel.INFO({msg: 'hello'});
console.log('res body', JSON.stringify(res.body)); ==>
loggel.INFO({msg: 'res body', params: {body: res.body}});

Build Up Test Suites

A big part of a developer responsibility is to test is creations for (at least major) bugs and unwanted behavior.
It’s true that testing is field of is own, but even if you’re not a tester, you must get at least familiar with it, there won’t be always someone else to test your code, plus, on many startup companies with small teams, testing your code, or someone else’s, will be done by you, or not done at all.

Test-Environment configuration

Mocha & Chai in the top-3 most dependents open source libraries

Mocha and Chai are great testing libraries to work with, Mocha is the main framework you’ll work with, and Chai is the assertion library.
Install them (as dev-dependencies) ;

npm install mocha @types/mocha chai @types/chai --save-dev

add to scripts property, on your package.json, a new test script :

"test": "SET NODE_ENV=test&& mocha --require ts-node/register server/tests/**/*.test.ts"

In this script you’re setting NODE_ENV value to be “test”, then directing mocha to execute all the files under server/tests folder, with a name that matches *.test.ts regex, (the other argument for compiling the test suites to js).

You can understand from this that all your test suites will be placed anywhere under server/tests folder, with any name that ends with “.test.ts”.

Test Suite Structure

A basic test implementation will be placed in a describe method, with a string describing what you’re testing in it, and a callback with the testing implementation that takes place.
The describe() block (the callback you provide it) contains one or more it() blocks, and similar to the describe() block, you provide to it() method a string describing what the is the expected test result (usually start with “should”) and a callback with the testing action.

Simple test suite example

Start Testing

For starters, to get the hang of it, let’s build a test suite for LanguagesManager class first.
Create under the server/tests folder, a new file named languages-manager.test.ts, with the following :

There are only three methods in LanguagesManager class, all relatively simple, each method is beaning tested on a separate it() method.
The string parameters for the describe() and it() methods explaining well what’s happening on each test.

On simple tests, usually with synchronous actions, the flow is pretty standard, first you arrange the parameters to perform the test on, then comes the act part, where you execute the testing action, and the last is to assert the testing result with what you expect to get.

running the ‘LanguagesManager’ test suite

Side-Note — For the testing result to be displayed like expected on the console, you must silence all other writhing to the console. With the logger service I provide you, that being handled automatically, this configuration should be simple to configure on most logger services.

API Route Testing

To test your API routes you’ll require another dependency, Supertest (Superagent) - HTTP assertions library, integrated perfectly with express framework.
Install supertest package (as dev-dependency) :

npm i supertest @types/supertest –-save-dev

Let’s go through an example of using Supertest in an environment similar to yours;

import * as request from 'supertest';
import { expect } from 'chai';
import { app } from '../server';
describe('GET /user', function() {
// 'done' parameter is a callback used on asynchronous testing.
it('respond with json', function(done) {
// requesting the app instance
request(app)
// calling the GET: /user API route
.get('/user')
// setting request header
.set('Accept', 'application/json')
// assert 'Content-Type' header
.expect('Content-Type', /json/)
// assert status code is 200
.expect(200)
// end() Perform the request and invoke the callback
.end((err, res) => {
// here you can use Chai assertion method
expect(res.body).to.have.property('users');
// calling done to alert Mocha the test has ended
// passing any error object in case you got one
done(err);
});
});
});

Asynchronous Tests with Mocha - By adding a callback (usually named done) to it(), Mocha will know that it should wait for this function to be called to complete the test. (from Mocha docs)

To perform those API tests you must first export your app instance, so the API calls could be invoked be Supertest from the test suites.

After examining a test suite example, using Mocha, Chai, and Supertest all together, we can continue to build the test suites for your actual API routes.

So first, add a little change on server.ts, at the end of the file, export your app instance ;

...
...
app.listen(process.env.PORT || PORT, () => {
console.log(`server running on port ${process.env.PORT || PORT}`);
})
export { app } // export for testing

Now, create a new file under server/tests folder, named app-routes.test.ts, and add to it your first test suite of GET: /langs API route,
try to write it on your own, based on the example above.

The comments explain each line, but in short, you’re asserting the standard request parameters, then on end() you’re asserting that the response body contains a langs property with the languageTable object as its value.

The next route to build a test suite for is POST: /run, it will be quite similar but a bit different.
The previous route, was of a GET method, it did not receive any specific parameters to validate, so its behavior was to remain the same for any incoming requests, there for you tested only one scenario.
On this route (POST: /run) test suite, there will be multiple scenarios for you to test, there are parameters attached to the incoming request’s body, and the request reacts differently, depends on those parameters validation status.

Here you’re testing four scenarios, the first is of the best case, where all the parameters are in place, the second is of a case where lang body property contains unsupported language, all the rest are variants of the second case.
You can easily come up with more scenarios to test if you think more is necessary, give it a try, and with it on own 🌴.

Result of all the test suites

Let’s review

  1. You integrated a logger service into your app.
  2. You set up a test environment - Mocha & Chai and build a test suite for the util service in your app.
  3. You installed Supertest package, walked through an example of building a test suite using Mocha & Supertest.
  4. Finally, you build test suites for your API routes.

You’re done building the test suites of your API’s routes,
On the next part — connecting the client to your server and exposing the code-editor functionality to be used in your app.

--

--