mockzilla-webextension icon indicating copy to clipboard operation
mockzilla-webextension copied to clipboard

Running same test twice lead to following error: Mock "port.onMessage" has been used after tests have finished!

Open kabeleced77 opened this issue 3 years ago • 5 comments

As already discussed in #13 I have followng code to be tested

import browser from 'webextension-polyfill'

export class GithubMockzillaMessaging {
  public connect(): void {
    browser.runtime.onConnect.addListener((port: browser.Runtime.Port) => {
      port.onMessage.addListener((message): void => {
        port.postMessage(`${message}: reply`)
      })
    })
  }
}

My unit test is following

import 'mockzilla-webextension'
// import of mockzilla-webextension before(!) actual import of 'webextension-polyfill' in system-under-test
import { mockEvent, MockzillaEventOf } from 'mockzilla-webextension'
import { deepMock } from 'mockzilla'
import { Runtime } from 'webextension-polyfill'
import { GithubMockzillaMessaging } from './../src/GithubMockzillaMessaging'

describe('BackgroundScriptMessaging', () => {
  const [port, mockPort, mockPortNode] = deepMock<Runtime.Port>('port')
  let msgs = new Array()
  let addListenerOnConnect: MockzillaEventOf<typeof mockBrowser.runtime.onConnect>
  let addListenerOnMessage: MockzillaEventOf<typeof mockPort.onMessage>

  beforeEach(() => {
    addListenerOnConnect = mockEvent(mockBrowser.runtime.onConnect)
    addListenerOnMessage = mockEvent(mockPort.onMessage)
  })

  describe('MessagingBackgroundScript function test', () => {
    it('method connect() shall handle connection request', async () => {
      mockPort.postMessage.expect('test-message: reply')
      const sut = new GithubMockzillaMessaging()
      sut.connect()
      addListenerOnConnect.emit(port)
      addListenerOnMessage.emit('test-message', port)
    })
    it('method connect() shall handle more connection requests', async () => {
      mockPort.postMessage.expect('test-message-2: reply')
      const sut = new GithubMockzillaMessaging()
      sut.connect()
      addListenerOnConnect.emit(port)
      addListenerOnMessage.emit('test-message-2', port)
    })
  })
})

The unit test code shall test several cases of the code. When the unit test code is executed with just one it()-case it works without errors. Adding - as shown above - a second it()-case the unit test execution breaks for second it()-case with following error

Mock "port.onMessage" has been used after tests have finished!

What did I miss? Thank you

kabeleced77 avatar Dec 29 '22 14:12 kabeleced77

Without looking at the rest of your code, I would assume, that you have asynchronous code, which continues running after the test function has completed. You'd have to stop that asynchronous code somehow.

Lusito avatar Jan 08 '23 15:01 Lusito

The code above is everything to be tested. It opens a messaging connection waiting. As soon as a message is received it replies sending back the received message appending the string ": reply". Running two UTs against this code the behaviour described above is triggered: Mock "port.onMessage" has been used after tests have finished!

As the first UT is passed and the second one complains, I thought maybe the mock-up must be "async" and maybe awaited...but here I'm kind of lost. Or maybe the first UT has to await "something" as the error says the mock port.onMessage has been used after tests have finished?

kabeleced77 avatar Jan 20 '23 19:01 kabeleced77

Sorry, I must have overlooked something back then. Your port mock needs to be disabled and enabled manually:

    beforeEach(() => mockPortNode.enable());
    afterEach(() => mockPortNode.verifyAndDisable());

See: https://lusito.github.io/mockzilla/deep-mock.html#solution

I guess the API could be written more comfortable for this scenario. Maybe like this: mockEvent(mockBrowser.runtime.onConnect, true); or: mockEvent(mockBrowser.runtime.onConnect, { autoCleanup: true });

Lusito avatar Jan 22 '23 11:01 Lusito

Great, works :).

Does that mean, the "Node"-object of a mocked object always requires to be called like this with enable() and verifyAndDisable()?

kabeleced77 avatar Jan 22 '23 12:01 kabeleced77

Aside from the mockbrowser one, which is preconfigured, yes.

Lusito avatar Jan 22 '23 19:01 Lusito