Home > Blockchain >  Mocking function to unit test Serverless Lambda
Mocking function to unit test Serverless Lambda

Time:08-20

I am really struggling to understand unit testing within a Serverless Application. So I obviously have my handler, and I have a single Lambda function

const responses = require('../utils/jsonResponse');
const someConnector = require('../services/connectToService/connectToService');

module.exports = async (event) => {
  const connectionParams = {
    //some env variables
  };

  try {
    const token = await someConnector.connectToService(connectionParams);
    return responses.status(token, 200);
  } catch (e) {
    return responses.status(
      `Issue connecting to service - ${e.message}`,
      500,
    );
  }
};

So this Lambda function is pretty straight forward, gets some environment variables, and awaits a response from a service. It then returns the response.

So I have already done integration tests for this which is fine, but now I wanted to do a Unit test. I wanted to test this function in isolation, so essentially I want to mock connectToService to return my own responses.

So I came up with the following

require('dotenv').config();

const { expect } = require('chai');
const sinon = require('sinon');
let sandbox = require("sinon").createSandbox();
const LambdaTester = require('lambda-tester');
const handler = require('../../../handler');
const msConnector = require('../../../services/connectToService/connectToService');

describe('Testing handler', async (done) => {
  describe('endpoint someEndpoint returns 200', () => {
    it('Should resolve with 200', async () => {
      before(() => {
        sandbox = sinon.createSandbox();
        sandbox.stub(msConnector, 'connectToService').resolves('some-token');
      });
      afterEach(() => {
        sandbox.restore();
      });
      await LambdaTester(handler.someEndpoint)
        .expectResult((result) => {
          console.log(result);
          expect(result.statusCode).to.equal(200);
        });
    });
  });
  done();
});

msConnector is the filename of the service, connectToService is the function name. What I want to do is not invoke this function, but return some-token when my Lambda calls it.

However, I have the console.log, and what I get from that is the real token, not some-token.

This tells me that the mocked function is really being called and executed and returning the real value.

So how can I mock this to make sure it returns some-token?

Thanks

Service function

const { DOMParser } = require('@xmldom/xmldom');
const axios = require('axios');
const { loginRequest } = require('./xml/login');


const connectToService = async (connectionParams) => {
  //this injects config details into XML
  const xmlRequest = loginRequest(
    connectionParams.username,
    connectionParams.password,
    connectionParams.url,
  );

  const config = {
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      'Content-Length': xmlRequest.length,
    },
  };

  const token = await axios
    .post(connectionParams.msHost, xmlRequest, config)
    .then((res) => {
      const dom = new DOMParser().parseFromString(res.data, 'text/xml');

      if (
        dom.documentElement
          .getElementsByTagName('wsse:secToken')
          .item(0)
      ) {
        return dom.documentElement
          .getElementsByTagName('wsse:secToken')
          .item(0).firstChild.nodeValue;
      }
      throw new Error('Invalid Username/Password');
    })
    .catch((err) => {
      throw new Error(`Error making connection - ${err.message}`);
    });

  return token;
};

module.exports = {
  connectToService,
};

CodePudding user response:

The function connectToService may be not same copy between you mocked and called. Because you overwrote a new object by module.exports = .... This causes you probably get different object for each require.

Try to do the below approach sharing the same object for all require.

const { DOMParser } = require('@xmldom/xmldom');
const axios = require('axios');
const { loginRequest } = require('./xml/login');


const connectToService = async (connectionParams) => {
  //this injects config details into XML
  const xmlRequest = loginRequest(
    connectionParams.username,
    connectionParams.password,
    connectionParams.url,
  );

  const config = {
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      'Content-Length': xmlRequest.length,
    },
  };

  const token = await axios
    .post(connectionParams.msHost, xmlRequest, config)
    .then((res) => {
      const dom = new DOMParser().parseFromString(res.data, 'text/xml');

      if (
        dom.documentElement
          .getElementsByTagName('wsse:secToken')
          .item(0)
      ) {
        return dom.documentElement
          .getElementsByTagName('wsse:secToken')
          .item(0).firstChild.nodeValue;
      }
      throw new Error('Invalid Username/Password');
    })
    .catch((err) => {
      throw new Error(`Error making connection - ${err.message}`);
    });

  return token;
};

module.exports.connectToService = connectToService;
  • Related