Joi: Joi import fails with Method Map.prototype.set called on incompatible receiver #<Map>

Created on 13 Apr 2020  ยท  7Comments  ยท  Source: sideway/joi

Support plan

  • which support plan is this issue covered by? (e.g. Community, Core, Plus, or Enterprise): Community
  • is this issue currently blocking your project? (yes/no): yes
  • is this issue affecting a production system? (yes/no): no

Context

  • node version: v12.11.1
  • module version: v17.1.1
  • environment (e.g. node, browser, native): node
  • used with (e.g. hapi application, another framework, standalone, ...): standalone
  • any other relevant information: I am using typescript & jest

How can we help?

Hi guys, I have a really strange issue and I am not able to understand what is going on.

I am trying to run unit tests with jest (v25.3.0). While I am importing @hapi/joi in one of my node files I get the following error:

  โ— Test suite failed to run

    TypeError: Method Map.prototype.set called on incompatible receiver #<Map>
        at Map.set (<anonymous>)

      at Object.<anonymous>.module.exports.internals.clone (../node_modules/@hapi/hoek/lib/clone.js:74:20)
      at Object.<anonymous>.module.exports.internals.clone (../node_modules/@hapi/hoek/lib/clone.js:99:31)
      at Object.<anonymous>.module.exports.internals.clone (../node_modules/@hapi/hoek/lib/clone.js:99:31)
      at Object.<anonymous>.module.exports.internals.clone (../node_modules/@hapi/hoek/lib/clone.js:99:31)
      at Object.<anonymous>.module.exports.internals.clone (../node_modules/@hapi/hoek/lib/clone.js:99:31)
      at Object.<anonymous>.exports.type (../node_modules/@hapi/joi/lib/extend.js:16:23)
      at Object.<anonymous>.internals.Base.extend (../node_modules/@hapi/joi/lib/base.js:437:23)
      at Object.<anonymous> (../node_modules/@hapi/joi/lib/types/alternatives.js:15:22)
      at Object.<anonymous> (../node_modules/@hapi/joi/lib/index.js:21:23)
      at Object.<anonymous> (app/controllers/v1/file-controller.ts:1353:41)

No test is running before the failure. The line in app/controllers/v1/file-controller.ts that is causing the issue is import * as Joi from '@hapi/joi';

Do you have any idea why this is happening, because I am stuck

support

Most helpful comment

@ptrin @derkinderfietsen here's what's happening:

file-controller.ts will be importing a mongoose model such as ../model/file.ts.

Then inside file-controller.test.ts, that model would be mocked using an automatic mock.

...
jest.mock('../../model/file.ts') // automatic mock inadvertently mocks Map
...
describe('file controller', () => {
  test('deletes file', async () => {
    await fileController.delete('filename') // this calls `model/file.ts`'s delete method
  })
  ...
})

What @ecoulson did was explicitly mock the used methods on model/file.ts using the a module factory.

...
jest.mock('../../model/file.ts', () => ({ delete: jest.fn() })) // Map no longer mocked
...
describe('file controller', () => {
  test('deletes file', async () => {
    await fileController.delete('filename') // this calls `model/file.ts`'s delete method
  })
  ...
})

All 7 comments

Hoek has some specific logic to handle cloning maps. From the error, it seems that this logic is somehow circumvented. Probably due to something the typescript compiler jest does.

Hey I am encountering the same error, when I clear my jest mocks the code executes. Have you found a solution to this issue?

Edit:

Based off of what kanongil said, the Map object maybe getting mocked by jest?

I found out that the issue is that the Map object is being mocked by Jest. I found that the Map class becomes mocked when I make a call to mock a mongoose model using jest.mock(...). I solved this issue by passing a factory argument to the jest.mock function that created my mongoose models without touching the Map class. All of my tests seem to be passing fine now.

@ecoulson I'm having the same issue here, could you please explain a bit more about the solution?

@ecoulson I'd also really like a little more detail on how you worked around this if it isn't too much trouble :)

@ptrin @derkinderfietsen here's what's happening:

file-controller.ts will be importing a mongoose model such as ../model/file.ts.

Then inside file-controller.test.ts, that model would be mocked using an automatic mock.

...
jest.mock('../../model/file.ts') // automatic mock inadvertently mocks Map
...
describe('file controller', () => {
  test('deletes file', async () => {
    await fileController.delete('filename') // this calls `model/file.ts`'s delete method
  })
  ...
})

What @ecoulson did was explicitly mock the used methods on model/file.ts using the a module factory.

...
jest.mock('../../model/file.ts', () => ({ delete: jest.fn() })) // Map no longer mocked
...
describe('file controller', () => {
  test('deletes file', async () => {
    await fileController.delete('filename') // this calls `model/file.ts`'s delete method
  })
  ...
})

@auzwang Thanks, that's very helpful!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Taxi4you picture Taxi4you  ยท  3Comments

JbIPS picture JbIPS  ยท  4Comments

REBELinBLUE picture REBELinBLUE  ยท  3Comments

longweiquan picture longweiquan  ยท  3Comments

kailashyogeshwar85 picture kailashyogeshwar85  ยท  4Comments