Serverless Integration Test with Offline Plugin

The Test we are going to create today will demonstrate adding an integration test (api test) to your serverless project.

The Tech Stack: NodeJS, Jest, Serverless Framework, and the Serverless offline plugin.

The test going to cover the /search route from my devresorces project.

In order to test a route when you develop serverless function you usually going to use the offline plugin to run the function locally and test the end point.

Lets add Jest to the project this is simple as that:

npm i -D jest

Now lets create the test folder and our first test dile inside /tests/search.test.js

//search.test.js
jest.setTimeout(30000); // incease jest timeout to 30s
const rp = require('request-promise'); // http library to create the http call
const { startSlsOffline, stopSlsOffline } = require('./slsUtils'); 
// utililty to start and stop the serverless offline plugin.

const domain = 'http://localhost:3001'; //make sue to chnage to the port you are using

beforeAll(async () => {
  await startSlsOffline().catch(e => {
    console.error(e);
  });
});

afterAll(() => {
  stopSlsOffline();
});

test('search', async () => {
  try {
    const res = await rp.get(`${domain}/search?q=jest`);
    expect(res).toBeDefined();
    const resources = JSON.parse(res);
    expect(typeof resources).toBe('object');
    expect(resources.length).toBeDefined();
  } catch (error) {
    throw error;
  }
});

Basically what we are doing is to run sls offline start  before the tests start and in the end we kill the process.

const { spawn } = require('child_process');

let slsOfflineProcess;

const finishLoading = () =>
  new Promise((resolve, reject) => {
    slsOfflineProcess.stdout.on('data', data => {
      if (data.includes('Offline listening on')) {
        console.log(data.toString().trim());
        console.log(`Serverless: Offline started with PID : ${slsOfflineProcess.pid}`);
        resolve('ok');
      }

      if (data.includes('address already in use')) {
        reject(data.toString().trim());
      }
    });

    slsOfflineProcess.stderr.on('data', errData => {
      console.log(`Error starting Serverless Offline:\n${errData}`);
      reject(errData);
    });
  });

function startSlsOffline() {
  const cmdArr = 'offline start --skipCacheInvalidation --port 3001 --stage dev'.split(' ');
  slsOfflineProcess = spawn('sls', cmdArr, { shell: true, cwd: process.cwd() });

  return finishLoading();
}

function stopSlsOffline() {
  slsOfflineProcess.stdin.write('q\n');
  slsOfflineProcess.stdin.pause();
  slsOfflineProcess.kill();

  console.log('Serverless Offline stopped');
}

module.exports = {
  stopSlsOffline,
  startSlsOffline
};

If you going to create many test files make sure to move the beforeAll and afterAll to the global config file.

You can also run this test in your ci and on the actual cloud lambda.

feel free to contact me for more details :)

Nir Adler

Nir Adler