Dave Sag
Dave Sag

Reputation: 13486

How do I use Jest to mock a promisified function?

I have a very simple little utility function xml2JSON as follows

import { promisify } from 'util'
import { parseString } from 'xml2js'

const xml2js = promisify(parseString)

const xmlToJSON = async xml => xml2js(xml)

export default xmlToJSON

I am trying to test it with Jest, mocking out the stuff I don't need to care about

import * as util from 'util'
import * as xml2js from 'xml2js'

import xmlToJSON from './xmlToJSON'

jest.mock('util')
jest.mock('xml2js')

describe('xmlToJSON', () => {
  const promisifiedParseString = jest.fn()
  util.promisify = jest.fn(() => promisifiedParseString)
  const js = { some: 'result' }
  const xml = '<some>result</some>'
  let result

  beforeAll(async () => {
    promisifiedParseString.mockResolvedValue(js)
    result = await xmlToJSON(xml)
  })

  it('promisified the original parseString', () => {
    expect(util.promisify).toHaveBeenCalledWith(xml2js.parseString)
  })

  it('called the promisified parseString with the xml', () => {
    expect(promisifiedParseString).toHaveBeenCalledWith(xml)
  })

  it('returned the expected result', () => {
    expect(result).toEqual(js)
  })
})

But I am getting the error

TypeError: xml2js is not a function

  4 | const xml2js = promisify(parseString)
  5 | 
> 6 | const xmlToJSON = async xml => xml2js(xml)
    |                                ^
  7 | 
  8 | export default xmlToJSON
  9 | 

What am I doing wrong?

Based on suggestion below I have tried changing the order of the imports:

import * as util from 'util'
import * as xml2js from 'xml2js'

jest.mock('util')
jest.mock('xml2js')
const promisifiedParseString = jest.fn()
util.promisify = jest.fn(() => promisifiedParseString)
import xmlToJSON from './xmlToJSON'

describe('xmlToJSON', () => {
  const js = { some: 'result' }
  const xml = '<some>result</some>'
  let result

  beforeAll(async () => {
    promisifiedParseString.mockResolvedValue(js)
    result = await xmlToJSON(xml)
  })

  it('promisified the original parseString', () => {
    expect(util.promisify).toHaveBeenCalledWith(xml2js.parseString)
  })

  it('called the promisified parseString with the xml', () => {
    expect(promisifiedParseString).toHaveBeenCalledWith(xml)
  })

  it('returned the expected result', () => {
    expect(result).toEqual(js)
  })
})

but it made no difference.

Upvotes: 4

Views: 3938

Answers (2)

elsoybean
elsoybean

Reputation: 41

I know this is old, but you need to also change

import xmlToJSON from './xmlToJSON'

to

const xmlToJSON = require('./xmlToJSON');

because imports get hoisted to the top of the script, and requires do not.

Upvotes: 0

tbking
tbking

Reputation: 9116

You need to change util.promisify behaviour before you import the file which is using it.

So the order should be something like:

util.promisify = jest.fn(() => promisifiedParseString)
import xmlToJSON from './xmlToJSON'

Upvotes: 1

Related Questions