Categories
Express JavaScript Testing

Adding Tests to Express Apps with Jest and Supertest

Automated tests are essential to the apps we write since modern apps have so many moving parts.

In this piece, we’ll look at how to write apps to test an Express app that interacts with a database with Jest and SuperTest.


Creating the App We’ll Test

We create a project folder by creating an empty folder and running the following to create a package.json file with the default answers:

npm init -y

Then we run the following to install the packages for our apps:

npm i express sqlite3 body-parser

Then, we create the app.js file for our app and write:

const express = require('express');  
const sqlite3 = require('sqlite3').verbose();  
const bodyParser = require('body-parser');  
const app = express();  
const port = process.env.NODE_ENV === 'test' ? 3001 : 3000;  
let db;  
if (process.env.NODE_ENV === 'test') {  
    db = new sqlite3.Database(':memory:');  
}  
else {  
    db = new sqlite3.Database('db.sqlite');  
}

db.serialize(() => {  
    db.run('CREATE TABLE IF NOT EXISTS persons (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)');  
});

app.use(bodyParser.json());  
app.get('/', (req, res) => {  
    db.serialize(() => {  
        db.all('SELECT * FROM persons', [], (err, rows) => {  
            res.json(rows);  
        });  
    })  
})

app.post('/', (req, res) => {  
    const { name, age } = req.body;  
    db.serialize(() => {  
        const stmt = db.prepare('INSERT INTO persons (name, age) VALUES (?, ?)');  
        stmt.run(name, age);  
        stmt.finalize();  
        res.json(req.body);  
    })  
})

app.put('/:id', (req, res) => {  
    const { name, age } = req.body;  
    const { id } = req.params;  
    db.serialize(() => {  
        const stmt = db.prepare('UPDATE persons SET name = ?, age = ? WHERE id = ?');  
        stmt.run(name, age, id);  
        stmt.finalize();  
        res.json(req.body);  
    })  
})

app.delete('/:id', (req, res) => {  
    const { id } = req.params;  
    db.serialize(() => {  
        const stmt = db.prepare('DELETE FROM persons WHERE id = ?');  
        stmt.run(id);  
        stmt.finalize();  
        res.json(req.body);  
    })  
})

const server = app.listen(port);  
module.exports = { app, server };

The code above has the app we’ll test.

To make our app easier to test, we have:

const port = process.env.NODE_ENV === 'test' ? 3001 : 3000;  
let db;  
if (process.env.NODE_ENV === 'test') {  
    db = new sqlite3.Database(':memory:');  
}  
else {  
    db = new sqlite3.Database('db.sqlite');  
}

So we can set the process.env.NODE_ENV to 'test' to make our app listen to a different port than it does when the app is running in a nontest environment.

We’ll use the 'test' environment to run our tests.

Likewise, we want our app to use a different database when running unit tests than when we aren’t running them.

This is why we have:

let db;  
if (process.env.NODE_ENV === 'test') {  
    db = new sqlite3.Database(':memory:');  
}  
else {  
    db = new sqlite3.Database('db.sqlite');  
}

We specified that when the app is running in a 'test' environment we want to use SQLite’s in-memory database rather than a database file.


Writing the Tests

Initialization the code

With the app made to be testable, we can add tests to it.

We’ll use the Jest test runner and SuperTest to make requests to our routes in our tests. To add Jest and SuperTest, we run:

npm i jest supertest

Then, we add app.test.js to the same folder as the app.js file we had above.

In app.test.js, we start by writing the following:

const { app } = require('./app');  
const sqlite3 = require('sqlite3').verbose();  
const request = require('supertest');  
const db = new sqlite3.Database(':memory:');
beforeAll(() => {  
    process.env.NODE_ENV = 'test';  
})

In the code above, we included our Express app from our app.js. Then we also included the SQLite3 and SuperTest packages.,

Then, we connected to our in-memory database with:

const db = new sqlite3.Database(':memory:');

Next, we set all the tests to run in the 'test' environment by running:

beforeAll(() => {  
    process.env.NODE_ENV = 'test';  
})

This will make sure we use port 3001 and the in-memory database we specified in app.js for each test.

To make our tests run independently and with consistent results, we have to clean our database and insert fresh data every time.

To do this, we create a function we call on each test:

const seedDb = db => {  
    db.run('CREATE TABLE IF NOT EXISTS persons (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)');  
    db.run('DELETE FROM persons');  
    const stmt = db.prepare('INSERT INTO persons (name, age) VALUES (?, ?)');  
    stmt.run('Jane', 1);  
    stmt.finalize();  
}

The code above creates the persons table if it doesn’t exist and deletes everything from there afterward.

Then we insert a new value in there to have some starting data.


Adding Tests

With the initialization code complete, we can write the tests.

GET request test

First, we write a test to get the existing seed data from the database with a GET request.

We do this by writing:

test('get persons', () => {  
    db.serialize(async () => {  
        seedDb(db);  
        const res = await request(app).get('/');  
        const response = [  
            { name: 'Jane', id: 1, age: 1 }  
        ]  
        expect(res.status).toBe(200);  
        expect(res.body).toEqual(response);  
    })  
});

We put everything inside the callback of db.serialize so the queries will be run sequentially.

First, we call seedDb, which we created above to create the table if it doesn’t exist, to clear out the database, and to add new data.

Then, we call the GET request by writing:

await request(app).get('/');

This gets us the res object with the response resolved from the promise.

request(app) will start the Express app so we can make the request.

Next, we have the response for us to check against for correctness:

const response = [  
  { name: 'Jane', id: 1, age: 1 }  
]

Then, we check the responses to see if we get what we expect:

expect(res.status).toBe(200);  
expect(res.body).toEqual(response);

The toBe method checks for shallow equality, and toEqual checks for deep equality. So we use toEqual to check if the whole object structure is the same.

res.status checks the status code returned from the server, and res.body has the response body.

POST request test

Next, we add a test for the POST request. It’s similar to the GET request test.

We write the following code:

test('add person', () => {  
    db.serialize(async () => {  
        seedDb(db);  
        await request(app)  
            .post('/')  
            .send({ name: 'Joe', age: 2 }); 
        const res = await request(app).get('/');  
        const response = [  
            { name: 'Jane', id: 1, age: 1 },  
            { name: 'Joe', id: 2, age: 2 }  
        ]  
        expect(res.status).toBe(200);  
        expect(res.body).toEqual(response);  
    })  
});

First, we reset the database with:

seedDb(db);

We made our POST request with:

await request(app)  
  .post('/')  
  .send({ name: 'Joe', age: 2 });

This will insert a new entry into the in-memory database.

Finally, to check for correctness, we make the GET request — like in our first test — and check if both entries are returned:

const res = await request(app).get('/');  
const response = [  
  { name: 'Jane', id: 1, age: 1 },  
  { name: 'Joe', id: 2, age: 2 }  
]  
expect(res.status).toBe(200);  
expect(res.body).toEqual(response);

PUT and DELETE tests

The test for the PUT request is similar to the POST request. We reset the database, make the PUT request with our payload, and then make the GET request to get the returned data, as follows:

test('update person', () => {  
    db.serialize(async () => {  
        seedDb(db);  
        await request(app)  
            .put('/1')  
            .send({ name: 'Joe', age: 2 }); 
        const res = await request(app).get('/');  
        const response = [  
            { name: 'Jane', id: 1, age: 1 }  
        ]  
        expect(res.status).toBe(200);  
        expect(res.body).toEqual(response);  
    })  
});

Then we can replace the PUT request with the DELETE request and test the DELETE request:

test('delete person', () => {  
    db.serialize(async () => {  
        seedDb(db);  
        const res = await request(app).delete('/1');  
        const response = [];  
        expect(res.status).toBe(200);  
        expect(res.body).toEqual(response);  
    })  
});

Running the Tests

To run the tests, we add the following to the scripts section:

"test": "jest --forceExit"

We have to add the --forceExit option so Jest will exist after the tests are run. There’s no fix for the issue where Jest tests using SuperTest don’t exit properly yet.

Then we run the following to run the tests:

npm test

And we should get:

PASS  ./app.test.js  
  √ get persons (11ms)  
  √ add person (2ms)  
  √ update person (2ms)  
  √ delete person (6ms)Test Suites: 1 passed, 1 total  
Tests:       4 passed, 4 total  
Snapshots:   0 total  
Time:        2.559s  
Ran all test suites.  
Force exiting Jest: Have you considered using `--detectOpenHandles` to detect async operations that kept running after all tests finished?

We should get the same thing no matter how many times we run the tests since we reset the database and made all database queries run sequentially.

Also, we used a different database and port for our tests than other environments, so the data should be clean.


Conclusion

We can add tests run with the Jest test runner. To do this, we have to have a different port and database for running the tests. Then we create the tables if they don’t already exist, clear all the data, and add seed data so we have the same database structure and content for every test.

With SuperTest, we can run the Express app automatically and make the request we want. Then, we can check the output.

Categories
JavaScript Rxjs

Rxjs Operators — Conditional and Booleans

Rxjs is a library for doing reactive programming. Creation operators are useful for generating data from various data sources to be subscribed to by Observers.

In this article, we’ll look at the defaultIfEmpty , every , find , findIndex and isEmpty operators.

defaultIfEmpty

The defaultIfEmpty operator returns an Observable that emits a value if the source Observable completes without emitting anything. Otherwise, the values from the source Observable are emitted.

It takes one optional value which is the defaultValue . The default for this is null . It’s the value that’ll be emitted if the source Observable is empty.

For example, we can use it as follows:

import { of } from "rxjs";  
import { defaultIfEmpty } from "rxjs/operators";  
of()  
  .pipe(defaultIfEmpty(1))  
  .subscribe(x => console.log(x));

We have an empty of() Observable, which will emit nothing before completing, so we’ll see 1 emitted.

every

The every operator returns an Observable that emits whether or not every item of the source satisfies the condition specified.

It takes up to 2 arguments. The first is a predicate function to determine if the item emitted by the source Observable meets a specified condition.

The second argument is the thisArg , which is optional and defaults to undefined . We set this to set the value of this in the predicate function.

We can use it as follows:

import { of } from "rxjs";  
import { every } from "rxjs/operators";  
const of$ = of(1, 2, 3, 4, 5, 6);  
const result = of$.pipe(every(val => val % 2 === 0));  
result.subscribe(x => console.log(x));

The code above checks if every value of the of$ Observable is an even number since we passed in val => val % 2 === 0 as the predicate function.

The result Observable emits false since not every number is even in the of$ Observable.

find

The find operator returns the first value emitted by the source Observable that matches the condition.

It takes up to 2 arguments. The first is a predicate function that returns the condition to check for matching the emitted values from the source Observable.

The second argument is the thisArg , which is optional and defaults to undefined . We set this to set the value of this in the predicate function.

We can use it as follows:

import { of } from "rxjs";  
import { find } from "rxjs/operators";  
const of$ = of(1, 2, 3, 4, 5, 6);  
const result = of$.pipe(find(val => val % 2 === 0));  
result.subscribe(x => console.log(x));

We should get 2 since it’s the first even number emitted by of$ , since we specified that val => val % 2 === 0 is the predicate that we want to check.

findIndex

The findIndex operator returns an Observable that emits of the first match of the value emitted from the source Observable that matches the condition returned by the predicate function.

It takes up to 2 arguments. The first is a predicate function that returns the condition to check for matching the emitted values from the source Observable.

The second argument is the thisArg , which is optional and defaults to undefined . We set this to set the value of this in the predicate function.

We can use it as follows:

import { of } from "rxjs";  
import { findIndex } from "rxjs/operators";  
const of$ = of(1, 2, 3, 4, 5, 6);  
const result = of$.pipe(findIndex(val => val % 2 === 0));  
result.subscribe(x => console.log(x));

We should get 1 since it’s the first index with an even number emitted by of$ , since we specified that val => val % 2 === 0 is the predicate that we want to check.

isEmpty

The isEmpty operator returns an Observable that emits false if the source Observable emit any values. Otherwise, true is emitted by the returned Observable.

It takes no arguments.

For example, we can use it as follows:

import { of } from "rxjs";  
import { isEmpty } from "rxjs/operators";  
const of$ = of();  
const result = of$.pipe(isEmpty());  
result.subscribe(x => console.log(x));

Then we should get true since of$ emits nothing.

The defaultIfEmpty operator returns an Observable that emits a default value if the source Observable emits nothing or emits the values of the source Observable if it emits something.

The every operator returns an Observable that emits whether or not every item of the source satisfies the condition specified.

The find and findIndex operators both search for the first value emitted by the source Observable that meets a condition, but find emits the object that meets the condition and findIndex emits the index of the object.

The isEmpty operator returns an Observable that emits true if it emits nothing and false otherwise.

Categories
JavaScript Rxjs

Rxjs Operators — Calculation and Aggregation

Rxjs is a library for doing reactive programming. Creation operators are useful for generating data from various data sources to be subscribed to by Observers.

In this article, we’ll look at some calculation and aggregation operators including count , max , min and reduce .

count

The count operator returns an Observable that counts the number of emissions on the source and emits the number when the source completes.

It takes an optional predicate function which returns a boolean with the condition for counting.

The predicate has 3 parameters, which is the value emitted by the source Observable, the index , which is the zero-based ‘index’ of the value from the source Observable, and the source , which is the source Observable itself.

For example, we can use it as follows to count the total number of emitted values:

import { of } from "rxjs";  
import { count } from "rxjs/operators";
const of$ = of(1, 2, 3, 4, 5, 6);  
const result = of$.pipe(count());  
result.subscribe(x => console.log(x));

The count operator without a predicate function passed in will count all emissions from the source Observable.

This should get us 6 since we have 6 values in the of$ Observable.

We can also pass in a predicate function as follows:

import { of } from "rxjs";  
import { count } from "rxjs/operators";
const of$ = of(1, 2, 3, 4, 5, 6);  
const result = of$.pipe(count(val => val % 2 === 0));  
result.subscribe(x => console.log(x));

Then we should get 3 since we’re only the number of even numbers emitted from of$ .

max

The max operator takes value from a source Observable that emits the numbers or items that can be compared with a comparer function, and gets the largest one from it and emits it with the returned Observable.

It takes an optional comparer function, which we can use to compare the value of 2 items.

For example, we can use it to get the highest number emitted from an Observable as follows:

import { of } from "rxjs";  
import { max } from "rxjs/operators";
of(2, 3, 4, 100)  
  .pipe(max())  
  .subscribe(x => console.log(x));

We should get 100 since 100 is the biggest number in the of(2, 3, 4, 100) Observable.

Also, we can pass in a function to the max operator to get the largest value from a list of objects as follows:

import { of } from "rxjs";  
import { max } from "rxjs/operators";

const people = [  
  { age: 17, name: "Joe" },  
  { age: 25, name: "Jane" },  
  { age: 19, name: "Mary" }  
];

of(...people)  
  .pipe(max((a, b) => a.age - b.age))  
  .subscribe(x => console.log(x));

Then we should get:

{age: 25, name: "Jane"}

from the console.log .

The comparer function works like the callback of the array’s sort method. We keep the order if comparer returns a negative number. We switch the order if comparer returns a positive number. Otherwise, they’re the same. Then the largest one is picked from the end.

min

The min operator is the opposite of the max operator. It gets the smallest value from the source Observable.

The arguments are the same as the max operator.

We can use it like the max operator as follows:

import { of } from "rxjs";  
import { min } from "rxjs/operators";
of(2, 3, 4, 100)  
  .pipe(min())  
  .subscribe(x => console.log(x));

Then we get 2.

Like the max operator, we can use the min operator with a comparer function as follows:

import { of } from "rxjs";  
import { min } from "rxjs/operators";  
const people = [  
  { age: 17, name: "Joe" },  
  { age: 25, name: "Jane" },  
  { age: 19, name: "Mary" }  
];  
of(...people)  
  .pipe(min((a, b) => a.age - b.age))  
  .subscribe(x => console.log(x));

Then we get:

{age: 17, name: "Joe"}

The comparer function works the same way as the one we pass into the max operator, except that the first one is picked instead of the last.

reduce

The reduce operator applies an accumulator function over all the values emitted by the source Observable to combine them into a single value, which is emitted by the returned Observable.

It takes up to 2 arguments. The first is the accumulator function, which is the function we use to combine the values.

The second argument is an optional seed value, which is the initial value of the accumulation.

For example, we can use it as follows:

import { of } from "rxjs";  
import { reduce } from "rxjs/operators";  
of(2, 3, 4, 100)  
  .pipe(reduce((a, b) => a + b, 0))  
  .subscribe(x => console.log(x));

The code above will sum up of the values from the of(2, 3, 4, 100) Observable and emits 109, which are the sum of 2, 3, 4, and 100.

a and b are the values emitted from the source Observable.

The count operator returns an Observable to count the number of times a source Observable emits or source Observable emitting a certain kind of value depending on the condition we specify.

min and max are used to get the minimum and maximum value according to a sorting function or from a group of numbers emitted respectively.

The reduce operator returns an Observable that takes the values from the source Observable and combine them into one in a way that we specify in the function we pass into it.

Categories
JavaScript JavaScript Basics

The Ultimate Guide to Manipulating Objects in JavaScript

Define New Object Literal

You can define object literals in JavaScript. An object does not have to an instance of a class in JavaScript.

You can define it like this:

const obj = { chicken: { hasWings: true }}

Define Object with Constructor

JavaScript lets you define objects that can be instantiated like a class with the new keyword.

You can define it like this:

const bird = function(hasWings){  this.hasWings = hasWings;}const chicken = new bird(true);  
console.log(chicken.hasWings); // true

Note the use of the function keyword instead of an arrow function. It is required to set this’s scope to the function itself.

Since ES6, you can define an object as an instance of a class.

For example:

class bird{  
  constructor(hasWings){  
    this.hasWings = hasWings;  
  }  
}const chicken = new bird(true);  
console.log(chicken.hasWings); // true

Get Keys of Object

Object.keys can be used to get all the top level keys of an object as strings. For example:

const chicken = { hasWings: true, bodyParts: [ {head: 1} ]};  
console.log(Object.keys(chicken)) // ['hasWings', 'bodyParts'];

Get Entries of an Object

Object.entriescan be used to get all the top level keys value entries of an object as arrays. For example:

const chicken = { hasWings: true, bodyParts: ['head', 'tail']};  
console.log(Object.entries(chicken)) // [['hasWings', true], ['bodyParts', ['head', 'tail']]];

Merge Two Objects

We can use the spread operation to combine two objects into one.

const a = {foo: 1};  
const b = {bar: 1};  
const c = {...a, ...b}; // {foo: 1, bar: 1}

If two objects have the same keys, the value of the one that is merged in last will override the earlier one.

const a = {foo: 1};  
const b = {bar: 1};  
const c = {bar: 2};  
const d = {...a, ...b, ...c};   
console.log(d) // {foo: 1, bar: 2}

Prevent Modification to an Existing Object

Object.freeze can be used to prevent an object from being modified. freeze takes an object as its argument and freezes an object in place.

For example:

let a = {foo: 1};  
a.foo = 2;  
Object.freeze(a);  
a.foo = 3;  
console.log(a) // {foo: 2}

Check If an Object Can Be Modified

Object.isFrozen can be used to check if an object is frozen by Object.freeze .

For example:

let a = {foo: 1};  
a.foo = 2;  
Object.freeze(a);  
a.foo = 3;  
console.log(Object.isFrozen(a)) // true

Clone Objects

If you assign an object to another variable, it just assigns the reference to the original object, so both variables will point to the original object. When one of the variables are manipulated, both will be updated. This is not always the desired behavior. To avoid this, you need to copy an object from one variable to another.

In JavaScript, this is easy to do. To shallow copy an object, we can use Objec.assign(), which is built into the latest versions of JavaScript. This function does a shallow copy, which means it only copies the top level of an object, while the deeper levels remain linked to the original object reference. This may not be desired if there is nested in your original object.

Here is an example of how to use Object.assign :

const a = { foo: {bar: 1 }}  
const b = Object.assign({}, a) // get a clone of a which you can change with out modifying a itself

You can also clone an array like this:

const a = [1,2,3]  
const b = Object.assign([], a) // get a clone of a which you can change with out modifying a itself

To do a deep copy of a object without a library, you can JSON.stringify then JSON.parse :

const a = { foo: {bar: 1, {baz: 2}}  
const b = JSON.parse(JSON.strinfy(a)) // get a clone of a which you can change with out modifying a itself

This does a deep copy of an object, which means all levels of an object are cloned instead of referencing the original object.

JSON.parse and JSON.stringify only works with plain objects, which means it cannot have functions and other code that runs.

With ES6, you can also use object destructuring to shallow clone objects, like so:

const a = { foo: {bar: 1}}  
const b = {...a} // get a clone of a which you can change with out modifying a itself

Object.keys

Object.keys gets the top level list of keys of an object and returns an array of them. For example:

const a = {foo: 1, bar: 2};  
const length = Object.keys(a).length // 2

Object.getPropertyNames

Object.getPropertyNames also gets a list of all top level of keys of an object and return them as an array. For example:

const a = {foo: 1, bar: 2};  
const length = Object.getOwnPropertyNames(a).length // 2

for…in Loop

There is a special loop for looping through the keys of an object. You can do the following:

const a = {foo: 1, bar: 2};  
let keysCount = 0;  
for (let key in a) {  
    keysCount++;  
}  
console.log(keysCount) // 2

hasOwnProperty

You can check if an object has a property by calling hasOwnProperty of an object. For example:

const a = {foo: 1, bar: 2};  
a.hasOwnProperty(key); // true

That’s it — a few simple steps for a few simple operations!

Categories
JavaScript JavaScript Basics

Check If an Array Contains and Item in JavaScript

There’re a few ways to check if an array contains an item in JavaScript.

We can use the includes, some, filter, find, findIndex, or indexOf to do the check. All methods are part of the array instance.

includes

The includes method takes in a value and returns true if it’s in the array or false otherwise. The check is done with === operator.

For instance, we can call includes as follows:

const arr = [1, 2, 3];
const result = arr.includes(1);

In the code above, we called includes on arr and then check if 1 is in it.

Therefore, it should return true so result is true.

some

The some method takes a callback with the condition of the item to check for. We can use it as follows:

const arr = [1, 2, 3];
const result = arr.some(a => a === 1);

In the code above, we passed in a callback to the some method to check if any array entry, which is the value of a is 1.

It returns true if it’s in the array and false otherwise, so we should get the same result.

filter

The filter method returns and array with the items that meet the condition returned by the callback included.

For instance, we can call it as follows:

const arr = [1, 2, 3];
const result = arr.filter(a => a === 1).length > 0;

In the code above, we called the filter method with the same callback, which returns an array with 1 in it. Then we get the length property of it and the make sure it’s bigger than 0.

Therefore, we get the same result as before by calling filter and adding the length check.

find

The find method returns the first entry in the array that meets the given condition returned in the callback.

If the item isn’t found, then undefined is returned.

For instance, we can write the following:

const arr = [1, 2, 3];
const result = typeof arr.find(a => a === 1) !== 'undefined'

In the code above, we use the typeof operator to check if the array item returned by find is undefined or not. If it’s not undefined, then it’s found. Otherwise, it’s not.

Then we should get the same result as before.

findIndex

The findIndex method returns the index of the first element in the array that meets the given condition returned in the callback.

If the item isn’t found, then -1 is returned.

For instance, we can write the following code to use findIndex:

const arr = [1, 2, 3];
const result = arr.findIndex(a => a === 1) !== -1;

In the code above, we called findIndex with the same callback used in the previous examples to search if any item in the array is 1. Then we check if the returned index isn’t -1.

Therefore, it should return the same boolean result as before, which is true.

indexOf

indexOf returns the index of the item that matches the value passed in as the argument.

It returns -1 if the value isn’t found.

For instance, we can use it as follows:

const arr = [1, 2, 3];
const result = arr.indexOf(1) !== -1;

We checked the returned array against -1 so that we can check if it’s in the array.

So we should get the same results as before.

some, find and findIndex are suitable for searching for any object, while the rest are suitable for primitive values since they let search with any condition instead of just letting us pass in a value or variable.

Objects are compared with the === operator so it doesn’t compare the structure of an object.