Skip to content

Instantly share code, notes, and snippets.

@dominikkaegi
Last active August 24, 2020 14:47
Show Gist options
  • Save dominikkaegi/7dd1a4b4dd2fc616b998084db40b2cc2 to your computer and use it in GitHub Desktop.
Save dominikkaegi/7dd1a4b4dd2fc616b998084db40b2cc2 to your computer and use it in GitHub Desktop.
How to change a mock implementation of an imported module without mokey patching?

1. Question concerning Mocking -> Mokey Patching / ES Modules

  • Why does monkey-patching only work for commonJS?
  • Or maybe broader what is an ES module situation?
  • What’s the difference between commonJS and ES Module JS?
  • What is their relation to ES versions?

2. Question concerning mocking whole Modules

How can you create different mock implementations of the same Module within the same test file? For example, we want to test the thumbWar implementation for the case that player1 wins but also for the case that player2 wins. So we need two mock implementations. With monkey patching, this is not a problem. But how we do it in an ES Modules situation?

Here is the code for thumbWar and getWinner for reference

//thumb-war.js
function thumbWar(player1, player2) {
  const numberToWin = 2
  let player1Wins = 0
  let player2Wins = 0
  while (player1Wins < numberToWin && player2Wins < numberToWin) {
    const winner = utils.getWinner(player1, player2)
    if (winner === player1) {
      player1Wins++
    } else if (winner === player2) {
      player2Wins++
    }
  }
  return player1Wins > player2Wins ? player1 : player2
}
//utils.js
// returns the winning player or null for a tie
function getWinner(player1, player2) {
  const winningNumber = Math.random()
  return winningNumber < 1 / 3
    ? player1
    : winningNumber < 2 / 3
      ? player2
      : null
}

A solution I found is to change the mock by useing the mockImplemetation property e.g:

utils.getWinner.mockImplementation((p1, p2) => p2)

But I have also read that the shortcut for the above is

utils.getWinner = jest.fn((p1, p2) => p2)

which in the end for me looks an awful lot again like monkey patching. Can you lift my confusion?

Here is the test code for reference:

const thumbWar = require('../thumb-war')
const utilsMock = require('../utils')

jest.mock('../utils', () => {
  return {
    getWinner: jest.fn((p1, p2) => p1)
  }
})

test('returns first winner', () => {
  const winner = thumbWar('Kent C. Dodds', 'Ken Wheeler')
  expect(winner).toBe('Kent C. Dodds')
  expect(utilsMock.getWinner.mock.calls).toEqual([
    ['Kent C. Dodds', 'Ken Wheeler'],
    ['Kent C. Dodds', 'Ken Wheeler']
  ])

  // cleanup
  utilsMock.getWinner.mockReset()
})


test('returns second winner', () => {
  // How to change the mock implementation without monkey patching?
  // Contradictionary to the mention "monkey patching does not work 
  // in an ES Module situation", in this test monkey patching is working,
  // but I assume that is only because we are using commonJS modules.
  utilsMock.getWinner = jest.fn((p1, p2) => p2)

  const winner = thumbWar('Kent C. Dodds', 'Ken Wheeler')
  expect(winner).toBe('Ken Wheeler')
  expect(utilsMock.getWinner.mock.calls).toEqual([
    ['Kent C. Dodds', 'Ken Wheeler'],
    ['Kent C. Dodds', 'Ken Wheeler']
  ])

  // cleanup
  utilsMock.getWinner.mockReset()
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment