Proper Jest Testing Azure Functions

2.8k Views Asked by At

I am wondering how to properly test Azure Functions with Jest. I have read the online documentation provided by MSoft but it's very vague, and brief. There are also some outdated articles I found that don't really explain much. Here is what I understand: I understand how to test normal JS async functions with Jest. And I understand how to test very simple Azure Functions. However I am not sure how to go about properly testing more complex Azure Functions that make multiple API calls, etc.

For example I have an HTTP Function that is supposed to make a few API calls and mutate the data and then return the output. How do I properly mock the API calls in the test? We only have one point of entry for the function. (Meaning one function that is exported module.exports = async function(context,req). So all of our tests enter through there. If I have sub functions making calls I can't access them from the test. So is there some clever way of mocking the API calls? (since actually calling API's during tests is bad practice/design)

Here is a sample of code to show what I mean

module.exports = async function (context, req)
{
    let response = {}

    if (req.body && req.body.id)
    {
        try
        {
            //get order details
            response = await getOrder(context, req)
        }
        catch (err)
        {
            response = await catchError(context, err);
        }
    }
    else
    {
        response.status = 400
        response.message = 'Missing Payload'
    }

    //respond
    context.res =
    {
        headers: { 'Content-Type': 'application/json' },
        status: response.status,
        body: response
    }
};

async function getOrder(context, req)
{
   //connection to db
   let db = await getDb() // <- how to mock this 

   //retrieve resource
   let item = await db.get...(id:req.body.id)... // <- and this

   //return 
   return {'status':200, 'data':item}
}


1

There are 1 best solutions below

3
Max Ivanov On

Consider this (simplified) example.

src/index.js (Azure Function entry point):

const { getInstance } = require('./db')

module.exports = async function (context) {
  // assuming we want to mock getInstance and db.getOrder
  const db = await getInstance()
  const order = await db.getOrder()

  return order
}

src/db.js:

let db

async function getInstance() {
  if (db === undefined) {
    // connect ...
    db = new Database()
  }

  return db
}

class Database {
  async getOrder() {
    return 'result from real call'
  }
}

module.exports = {
  getInstance,
  Database,
}

src/__tests__/index.test.js:

const handler = require('../index')
const db = require('../db')

jest.mock('../db')

describe('azure function handler', () => {
  it('should call mocked getOrder', async () => {
    const dbInstanceMock = new db.Database() // db.Database is already auto-mocked
    dbInstanceMock.getOrder.mockResolvedValue('result from mock call')

    db.getInstance.mockResolvedValue(dbInstanceMock)

    const fakeAzureContext = {} // fake the context accordingly so that it triggers "getOrder" in the handler
    const res = await handler(fakeAzureContext)

    expect(db.getInstance).toHaveBeenCalledTimes(1)
    expect(dbInstanceMock.getOrder).toHaveBeenCalledTimes(1)
    expect(res).toEqual('result from mock call')
  })
})
> jest --runInBand --verbose

 PASS  src/__tests__/index.test.js
  azure function handler
    ✓ should call mocked getOrder (4 ms)

For a complete quickstart, you may want to check my blog post