node.jsasync-awaitmocha.jsfsnode-promisify

How do I use async/await in a mocha before hook?


I am using node's util.promisify to try and await an fs.readFile result inside a helper function, but the second readFile is never called and I always get a timeout error.

From what I can see I am using await correctly, according to the Mocha docs and this blog post that explains the promisify utility function.

// mocha setup.js file
const { readFile } = require('fs')
const { promisify } = require('util')

module.exports = {
  readFile: promisify(readFile)
}
// test-file.js
const { assert } = require('chai')
const { readFile } = require('./setup')
const { CustomWordList, Spelling, Word } = require('../src/classes')
const nspell = require('nspell')

describe('Spelling Class', function () {
  const content = 'A colorful text that should be colorful cleaned during Spelling class instantiation! 1337'
  let dictionary
  let speller
  let obj

  before(async function () {
    this.timeout(5000)
    dictionary = await loadDictionary('en-au')
    speller = nspell(dictionary)
    obj = new Spelling(speller, content)
  })

  it('should assert object is instance of Spelling', function () {
    assert.instanceOf(obj, Spelling)
  })

  // read dictionary aff and dic files from disk and return an dictionary object
  const loadDictionary = async (filename) => {
    const dict = {}
    await readFile(`dictionaries/${filename}.dic`, 'utf8', (err, data) => {
      if (err) console.log(err)
      if (data) {
        dict.dic = data
        console.log('got dic data')
      }
    })
    await readFile(`dictionaries/${filename}.aff`, 'utf8', (err, data) => {
      if (err) console.log(err)
      if (data) {
        dict.aff = data
        console.log('got aff data')
      }
    })
    return dict
  }
})

The timeout error is the standard "timeout exceeded... ensure done() is called or ensure Promise resolves". I have noticed that the console will output "got dic data" if the first readFile is reading the .dic file, but if a swap the readFile operations, the console output is "got aff data".

This would suggest that for some reason only the first readFile is being executed, but I have no idea why the first readFile would block the second read file from being executed (and thus the return statement from ever being run).

Thanks for your time.


Solution

  • You're doing it wrong. After promisifying, your readFile function will return a Promise instead, and you can use async/await to handle this. If you use callback then you shouldn't need to promisify.

    Here's your loadDictionary function written with async/await.

    const loadDictionary = async (filename) => {
        const dict = {}
    
        try {
            const data = await readFile(`dictionaries/${filename}.dic`, 'utf8');
            if (data) {
                dict.dic = data
                console.log('got dic data')
            }
        } catch (err) {
            console.log(err)
        }
    
        try {
            const data = await readFile(`dictionaries/${filename}.aff`, 'utf8');
            if (data) {
                dict.aff = data
                console.log('got aff data')
            }
        } catch (err) {
            console.log(err)
        }
    
        return dict
    }