Fecha de publicación

What is a mock? 🤔

Autores

Alt Text

What is a mock?

Following Facebook's(aka jest) definition of mock:

Mock functions allow you to test the links between code by erasing the actual implementation of a function, capturing calls to the function (and the parameters passed in those calls), capturing instances of constructor functions when instantiated with new, and allowing test-time configuration of return values.

This definition can be a bit confusing for someone, but the goal for mocking is to replace something we don't control with something we do.

What can we do with a mock? (with real examples 👨‍🔧)

Capture calls 👮‍♂️

e.g With dependency injection 💉

import { screen, render } from '@testing-library/react'
import userEvent from '@testing-library/user-event'

describe('Capture calls onSubmit', () => {
  it('captures the `onSubmit` function call', () => {
    const onSubmit = jest.fn()
    const expectedOnSubmitProps = { name: 'Baki' }

    // GIVEN my form is rendered
    render(<MyComponent onSubmit={onSubmit} />)

    // WHEN submitting the form
    userEvent.click(screen.getByText('Submit'))

    // THEN the given prop is called
    expect(onSubmit).toHaveBeenCalledWith(expectedOnSubmitProps)
  })
})

e.g Without dependency injection 💉

TBD

Set return values 🚚

e.g: Static value⚓️

jest.mock('contexts/MediaQueryContext', () => ({
  ...jest.requireActual('contexts/MediaQueryContext'),
  useMedia: () => ({ isLarge: true, isPrint: false }), // will return always these values on each test case
}))

describe('Set return values for useMedia hook', () => {
  it('returns `true` for `isLarge` and `false` for `isPrint`', () => {
    // ...
  })
})

e.g: Dynamic value 🧬

import { useMedia } from 'contexts/MediaQueryContext'

jest.mock('contexts/MediaQueryContext', () => ({
  ...jest.requireActual('contexts/MediaQueryContext'),
  useMedia: jest.fn(), // we need to tell jest that useMedia will be mocked, otherwise it won't be mocked 😢
}))


describe('Set return values for useMedia hook', () => {
  const useMediaMocked = useMedia as jest.Mock // in TS we need to cast it to be able to access jest mock utilities, otherwise it will complain about that mock does not contains the expected method 😅

  it('returns true for isLarge and false for isPrint', () => {
    useMediaMocked.mockImplementation(() => ({
      isLarge: true,
      isPrint: false,
    }))

    // ...
  })

  it('returns false for isLarge and true for isPrint', () => {
    useMediaMocked.mockImplementation(() => ({
      isLarge: false,
      isPrint: true,
    }))

    // ...
  })
})

Change the implementation of a function, module, variable etc. 🧙🏼‍♂️

TBD

Useful tricks and tips

  • If your expected mock is not working, make sure that the path that you are mocking is the same path as the component is importing.

    e.g:

    // MyComponent.tsx
    import { useMedia } from 'contexts/MediaQueryProvider'
    
    function MyComponent() {
      const { isLarge } = useMedia()
    
      return isLarge ? 'Hola' : 'Adios'
    }
    
    // MyComponent.test.tsx
    
    // ❌ WRONG PATH! ❌
    /* 
      jest.mock('contexts/MediaQueryContext/MediaQueryContext', () => ({
        ...jest.requireActual('contexts/MediaQueryContext/MediaQueryContext'),
        useMedia: () => ({ isLarge: true }),
      }));
    */
    
    // ✅ CORRECT PATH! ✅
    jest.mock('contexts/MediaQueryContext', () => ({
      ...jest.requireActual('contexts/MediaQueryContext'),
      useMedia: () => ({ isLarge: true }),
    }))
    
    test('MyComponent', () => {
      render(<MyComponent />)
    
      expect(screen.getByText('Jelou')).toBeInTheDocument()
    })
    
  • Dynamic mock value (in the same test case 🤯) ⚠️ DISCLAIMER: USE IT AT YOUR OWN RISK (prefer to find a better approach to test your scenario) ⚠️

    TBD