ResizeObserver Mock (Vitest, ES Module 환경)

vi.mock() 은 파일 맨 위로 끌어올려져 바깥 변수를 못 본다 — 그래서 vi.hoisted() 로 모킹 함수를 미리 선언해 넘긴다.1

import { vi } from 'vitest'

const { mockResizeObserver, MockResizeObserver } = vi.hoisted(() => {
  let observers: { callback: ResizeObserverCallback; observer: any }[] = []

  // new 로 불리므로 화살표 함수가 아니라 function — 화살표면 "is not a constructor"
  const MockResizeObserver = vi.fn().mockImplementation(function (callback) {
    const observer = {
      observe: vi.fn(),
      unobserve: vi.fn(),
      disconnect: vi.fn(),
    }
    observers.push({ callback, observer })
    return observer
  })

  return {
    MockResizeObserver,
    mockResizeObserver: {
      triggerResize: (entries: ResizeObserverEntry[], index = 0) => {
        observers[index]?.callback(entries, observers[index].observer)
      },
      reset: () => {
        observers = []
      },
    },
  }
})

vi.mock('resize-observer-polyfill', () => ({ default: MockResizeObserver }))

// 테스트에서 사용
beforeEach(() => mockResizeObserver.reset())

it('should handle resize', () => {
  const mockEntry = {
    target: document.createElement('div'),
    contentRect: { width: 100 },
  }
  mockResizeObserver.triggerResize([mockEntry])
})

핵심: ES Module에서는 vi.hoisted() 필수. 여러 observer는 배열로 추적.


Footnotes

  1. vi.mock - Vitest: “The call to vi.mock is hoisted, so it doesn’t matter where you call it. … If you need to reference some variables outside of its scope, you can define them inside vi.hoisted and reference them inside vi.mock.” / vi.spyOn: “If you provide an arrow function, you will get <anonymous> is not a constructor error when the mock is called.” ↩

#514