ts-mockito icon indicating copy to clipboard operation
ts-mockito copied to clipboard

Promise hangs when resolving a mocked instance

Open hirikarate opened this issue 5 years ago • 10 comments

When a Promise wraps a mocked instance, it never resolves but hangs.

const { mock, instance } = require('ts-mockito')

const MockConnector = mock()
const connector = instance(MockConnector)

function connectorFactory() {
  return Promise.resolve(connector) // <== This mock instance (Proxy object) never get resolved by Promise.
}

(async () => {
  const created = await connectorFactory() // <== This Promise hangs forever.
  console.log('Connector created') // <== Never reached
})()

This issue is same with #155 which never gets noticed nor replied. A runnable sandbox: https://runkit.com/embed/5ljjgubxm9fm

hirikarate avatar Sep 28 '19 17:09 hirikarate

I can propose a fix: to add "then" to excluded property at this line in Mock.ts

private excludedPropertyNames: string[] = ["hasOwnProperty", "then"];

It is because Promise and functions marked with "async" keyword look for "then" method in resolved/returned value, if "then" exists as a function, they will regard the value as a Promise and attempt to resolve it.

hirikarate avatar Sep 28 '19 17:09 hirikarate

For now, this is my workaround

JavaScript:

const {
  mock, // Don't use this
  instance,
} = require('ts-mockito')
const { Mocker } = require('ts-mockito/lib/Mock') // Must be below require('ts-mockito')

function betterMock(clazz) {
    const mocker = new Mocker(clazz)
    mocker['excludedPropertyNames'] = ['hasOwnProperty', 'then']
    return mocker.getMock()
}

TypeScript:

import {
  mock, // Don't use this
  instance
} from 'ts-mockito'
import { Mocker } from 'ts-mockito/lib/Mock' // Must be below import 'ts-mockito'

function betterMock<T>(clazz?: (new(...args: any[]) => T) | (Function & { prototype: T }) ): T {
    const mocker = new Mocker(clazz)
    mocker['excludedPropertyNames'] = ['hasOwnProperty', 'then']
    return mocker.getMock()
}

Useage example:

const {
  mock, // Don't use this
  instance,
} = require('ts-mockito')
const { Mocker } = require('ts-mockito/lib/Mock') // Must be below require('ts-mockito')

function betterMock(clazz) {
    const mocker = new Mocker(clazz)
    mocker['excludedPropertyNames'] = ['hasOwnProperty', 'then']
    return mocker.getMock()
}

const MockConnector = betterMock() // Instead of mock()
const connector = instance(MockConnector)

function connectorFactory() {
  return Promise.resolve(connector) // <== This mock instance can be resolved by Promise.
}

(async () => {
  const created = await connectorFactory() // <== This Promise resolves.
  console.log('Connector created') // <== Reached!
})()

hirikarate avatar Sep 28 '19 17:09 hirikarate

@hirikarate your workaround works for me. You saved my day. Thank you very much for sharing!!

yingzhanguipath avatar Oct 15 '19 23:10 yingzhanguipath

oy, this is my issue...

xenoterracide avatar Dec 24 '19 21:12 xenoterracide

One solution could be to expose the excluded properties list in an options argument to the mock() function - something like mock(SouldNotBeThenable, { omitHandlersForProperties: [ 'then' ] }). This might be better than simply adding 'then' to the excluded properties list so that mocking the then() method is still allowed.

restjohn avatar Jan 25 '20 21:01 restjohn

Are there any updates on this issue?

FreifeldRoyi avatar Apr 26 '20 17:04 FreifeldRoyi

@NagRock why this issue has not been addressed for such a long time?

murbanowicz avatar Oct 02 '20 06:10 murbanowicz

Don't like to spam, but it may have some value - it works without an issue with @johanblumenberg fork.

At this moment this is only issue which holds some projects which I am involved in from switching back to main repo.

murbanowicz avatar Oct 26 '20 14:10 murbanowicz

Your workaround works for my case as well. Thanks! @hirikarate

ryejoon avatar Apr 06 '22 20:04 ryejoon