Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How To Reset Manual Mocks In Jest

I have a manual mock of crypto that looks like this:

// __mocks__/crypto.js

const crypto = jest.genMockFromModule('crypto')
const toString: Function = jest.fn(() => {
  return {}.toString()
})
const mockStringable = {toString}
const update: Function = jest.fn(() => mockStringable)
const deciper = {update}
crypto.createDecipheriv = jest.fn(() => deciper)

export default crypto

Which is basically tested like this:

const crypto = require('crypto')
jest.mock('crypto')

describe('cookie-parser', () => {
  afterEach(() => {
    jest.resetAllMocks()
  })
  describe('decryptCookieValue', () => {
    it('should call the crypto library correctly', () => {
      const result = decryptCookieValue('test-encryption-key', 'test-encrypted-value')
      expect(crypto.pbkdf2Sync).toHaveBeenCalledTimes(2)
      expect(crypto.createDecipheriv).toHaveBeenCalled()
      // more tests, etc, etc, etc
      expect(crypto.createDecipheriv('', '', '').update).toHaveBeenCalled()
      expect(result).toEqual({}.toString())
    })
  })

  ...

This works however if in that same test file, I test another method that invokes decryptCookieValue from within crypto.createDecipheriv no longer returns my mock decipher. Instead it returns undefined. For instance:

describe('cookie-parser', () => {
  afterEach(() => {
    jest.resetAllMocks()
  })
  describe('decryptCookieValue', () => {
    it('should call the crypto library correctly', () => {
      const result = decryptCookieValue('test-encryption-key', 'test-encrypted-value')
      expect(crypto.pbkdf2Sync).toHaveBeenCalledTimes(2)
      expect(crypto.createDecipheriv).toHaveBeenCalled()
      expect(crypto.createDecipheriv('', '', '').update).toHaveBeenCalled()
      expect(result).toEqual({}.toString())
    })
  })
  ...
  ...
  describe('parseAuthenticationCookie', () => {
    it('should create the correct object', () => {

      // parseAuthenticationCookie calls decryptCookieValue internally

      const result = parseAuthenticationCookie('', '') // Fails because internal call to crypto.createDecipheriv stops returning mock decipher.
      expect(result).toEqual({accessToken: null})
    })
  })
})

I think this is an issue with resetting the manual mock because if I take that later test and move it into a file all by itself with the same surrounding test harness it works just fine.

// new test file
import crypto from 'crypto'
import { parseAuthenticationCookie } from './index'

jest.mock('crypto')

describe('cookie-parser', () => {
  afterEach(() => {
    jest.resetAllMocks()
  })
  describe('parseAuthenticationCookie', () => {
    it('should create the correct object', () => {

      // Works just fine now

      const result = parseAuthenticationCookie('', '')
      expect(result).toEqual({accessToken: null})
    })
  })
})

Is my assessment here correct and, if so, how do I reset the state of the manual mock after each test?

like image 238
Brandon Tom Avatar asked Mar 26 '18 15:03

Brandon Tom


People also ask

How do I reset my mocks in Jest?

beforeEach(() => { jest. resetAllMocks(); }); “restoreMocks”: true : restores methods that were spied using jest. spyOn( .. ) to its original method.

How do you reset mock Before each test in Jest?

afterEach(() => { jest. clearAllMocks(); }); to call jest. clearAllMocks to clear all mocks after each test.

How do you clear mock implementation Jest?

Jest mockReset/resetAllMocks vs mockClear/clearAllMocks mockClear() does, and also removes any mocked return values or implementations. This is useful when you want to completely reset a mock back to its initial state. (Note that resetting a spy will result in a function with no return value).

How do I reset Jest?

To reset or clear a spy in Jest, we call jest. clearAllMocks . For instance, we write: afterEach(() => { jest.


Video Answer


1 Answers

From Jest docs: Does everything that mockFn.mockClear() does, and also removes any mocked return values or implementations. ref: https://jestjs.io/docs/en/mock-function-api#mockfnmockreset

In your example you are assuming that calling resetAllMocks will set your manual mock back and it's not.

The reason why your test works in a separate file is because jest runs each file isolated, which is nice since you can screw up only the specs living in the same file.

In your particular case something that might work is calling jest.clearAllMocks() (since this will keep the implementation and returned values). clearMocks options is also available at the jest config object (false as default), if you want to clear all your mocks on every test, this might be handy. Hope this help you or anyone else having having a similar issue.

Bonus tip (no quite related) If you are mocking a module that it's being used internally by other module and in some specific test you want to mock that module again with a different mock, make sure to require the module that it's using the mocked module internally again in that specific test, otherwise that module will still reference the mock you specified next to the imports statements.

like image 110
Pablo Alice Avatar answered Oct 17 '22 08:10

Pablo Alice