AWS Lambda Functions (Node.js) - Unit testing using Mocha and Chai
Tags: javascript, aws, webdev, testing
Published: 2021-12-17 14:17:45

Unit testing is the process of testing a piece of code or module and identifying any issues within it. In unit testing, we test a piece of code/module with some set of test cases. In JavaScript-based applications, we use generally use Mocha and Chai to write the test cases.

Let us learn how to write unit test cases for the AWS Lambda function using Mocha and Chai.

### What is AWS Lambda?

> AWS Lambda is a serverless, event-driven compute service that lets you run code for virtually any type of application or backend service without provisioning or managing servers.

Unit testing of AWS lambda functions is also important as it helps us find any issues in the lambda code. In this article, we will be learning more about unit testing the lambda functions.

Let’s get started 🚀

### Lambda Code

Let us first take a look into a sample Lambda function that fetches the search results from the database based on the search input and filter. This function takes two inputs — `searchText` and `filterBy`.

- `searchText` will have the text which we want to search in the database
- `filterBy` will have the field name based on which we want to order the search results.

```javascript
// app.js
async function lambdaFunction(event, context) {
  const response = {};
  try {
    let requestBody = event.body;
    let { searchText, filterBy } = requestBody;

    /* Validate the required request params */
    if (!utils.isValidateInput(searchText) || !utils.isValidateInput(filterBy)) {
      throw new Error("Invalid request body");
    }

    // Get search results
    const searchResult = await fetchSearchResult(searchText, filterBy);

    if (searchResult && searchResult.length > 0) {
      response.data = searchResult;
      response.message = "Results fetched!";
    } else {
      response.data = searchResult || [];
      response.message = "No results found";
    }
    response.code = 200;
    return response;
  } catch (error) {
    response.code = 400;

    if (error) {
      response.ErrorMessages = [error.message];
    }

    return response;
  }
}
```

### Unit testing of the lambda function

Now let’s test the above lambda code by writing the test cases. For unit testing, we will be using the following packages.

- [Mocha](https://mochajs.org/) - For creating a test suite and running the test cases.
- [Chai](https://www.chaijs.com/) - Assertion library to verify if a given code is working correctly or not.
- [Proxyquire](https://www.npmjs.com/package/proxyquire) - A package that can proxy the dependencies by overriding the functions inside it.
- [Lambda Tester](https://www.npmjs.com/package/lambda-tester) — A package that can help to run a lambda function locally.

Now let’s dive deep into it.

### Lambda function execution

For testing the lambda function, we need a way to execute/call a lambda function from our local. For that, we can use a third-party package called [lambda-tester](https://www.npmjs.com/package/lambda-tester). Lambda-tester can take an `event` object and execute the lambda function to return the result.

```javascript
// test.js
const expect = require("chai").expect;
const lambdaTester = require("lambda-tester");

// Import lambda funcion
const lambda = require('../src/lambda/app.js');

const mockData = {
  // some mock data
}

// Execute lambda function using lambda-tester package
lambdaTester(lambda.handler)
  .event(mockData) // Passing input data
  .expectResult((result) => {
    // Check if code exist
    expect(result.code).to.exist;

    // Check if code = 200
    expect(result.code).to.equal(200);

    // Check if data exist
    expect(result.data).to.exist;

    // Check if data is an array
    expect(result.data).to.be.a("array");

    done();
  })
  .catch(done); // Catch assertion errors
```

### Overriding and mocking the dependencies

Now that we know how to call/execute a lambda function in our local. Let us learn about mocking the dependencies inside the lambda function. These dependencies can be any third-party libraries or DB call or even an API call. To override and mock these dependencies we can use [proxyquire](https://www.npmjs.com/package/proxyquire) package.

Proxyquire will help us import the lambda function without calling (invoking) it and also help us mock the dependencies used inside the lambda function.

In the below example, we have two dependencies — _utils_ (utility functions) and _dataService_ (database functions). We will override a database function called _query_ and add our own logic which will return the mock results. However, we will not override the utility functions file (_utils_) as it has independent code without any third-party dependencies.

```javascript
// test.js
const proxyquire = require("proxyquire");

// Internal dependencies
const utils = require("../src/utils/utils");

// Create a object which will have mock functions
const dataStub = {
  // Mocking DB call
  query: function (params) {
    // return some mock results
  },
};

// Exporting the lambda with mock dependencies
const lambda = proxyquire.noCallThru().load("../src/lambda/app.js", {
  // Replacing the dependencies present inside lambda function (app.js) with mock functions
  "../dataService/data": dataStub,
  "../utils/utils": utils,
});
```

### Test case for the lambda function

Now that we have learned how to invoke a lambda function in local with mock dependencies. Let us now write a simple test case.

```javascript
// test.js
const expect = require("chai").expect;
const proxyquire = require("proxyquire");
const lambdaTester = require("lambda-tester");

// Internal dependencies
const utils = require("../src/utils/utils");

// Import mock function from mock.js
const { mockDBfunction, validInput, invalidInput } = require("./mock");

// Define a common test suite
describe("FetchSearchResult Lambda Unit Test", function () {
  let lambda = null;

  // Mocking data services
  let dataStub = {};

  beforeEach(function () {
    // Exporting the lambda with mock dependencies
    lambda = proxyquire.noCallThru().load("../src/lambda/app.js", {
      // Replacing the dependencies present inside lambda function (app.js) with mock functions
      "../dataService/data": dataStub,
      "../utils/utils": utils,
    });
  });

  describe("Successful Invocation", function () {
    let mockData = null;

    before(function () {
      // Attach mock function to data services (mocked)
      dataStub = {
        ...dataStub,
        // Mocking DB call
        query: function (params) {
          // Get the name of the function which is calling 'query' inside lambda function (app.js)
          let functionName = arguments.callee.caller.name;

          // based on the function name mock the data
          return mockDBfunction(functionName);
        },
      };
      // Get valid inputs from mock.js
      mockData = validInput();
    });

    it("with code = 200", function (done) {
      // Execute lambda function using lambdaTester package
      lambdaTester(lambda.handler)
        .event(mockData) // Passing input data
        .expectResult((result) => {
          // Check if code exist
          expect(result.code).to.exist;

          // Check if code = 200
          expect(result.code).to.equal(200);

          // Check if data exist
          expect(result.data).to.exist;

          // Check if data is an array
          expect(result.data).to.be.a("array");

          done();
        })
        .catch(done); // Catch assertion errors
    });
  });
});
```

Now let’s run the test case using the command `mocha` and check if it is passing or not.

![Unit Test Result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sr7xysvr7yj053repth3.png)

You can find all the code discussed in this article in [this](https://github.com/SyedAfrozPasha/node-lambda-unit-test) repo.

That’s all I have for today. Hope you enjoyed it. 😉

Thank you for stopping by. If you like the content do support me and follow me for more content like this.

<a href="https://www.buymeacoffee.com/SyedAfrozPasha"><img src="https://img.buymeacoffee.com/button-api/?text=Buy me a coffee&emoji=&slug=SyedAfrozPasha&button_colour=FFDD00&font_colour=000000&font_family=Cookie&outline_colour=000000&coffee_colour=ffffff"></a>

You can connect with me on [LinkedIn](https://www.linkedin.com/in/syedafrozpasha/), [Twitter](https://twitter.com/SyedAfroz_Pasha), and [GitHub](https://github.com/SyedAfrozPasha).