2

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}
}


search-learn
  • 1,037
  • 1
  • 9
  • 23
  • the answer would depend on where does the `getDb` comes from. Probably form some module, so it will be a good idea to include the import statements – Teneff Nov 18 '20 at 14:31

1 Answers1

0

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

Max Ivanov
  • 5,695
  • 38
  • 52
  • If we're mocking the db call then that actual line of db code is not executed, right? for example if in my `db.js` i misspelled my collection name instead of `apples` it's `appls` then how does that error get caught by the mock function if it never called my real db to check for a collection called apples instead of appls. Also you are exporting every function called by your main. What happens when you cant export them? Like lets say I have a `api.js` file that gets a connection but then in my AZ func i make 3 api calls ,w/ that singleton, those can't be exported, so how would that look like? – search-learn Nov 18 '20 at 22:24
  • You have a class defined with methods and all. What if someone isn't using a class @Max Ivanov? – search-learn Nov 18 '20 at 22:26
  • I made an assumption about the internals of the module to be mocked. The example shows how to mock a class and a function and how to use it in a test for an Azure function. Looks like you have a few questions about mocking with Jest in general and I believe those should be addressed separately. – Max Ivanov Nov 18 '20 at 22:43