Я тестировал react-redux
useSelector
с поверхностным монтажом фермента с использованием метода насмешки spyOn
от Jest. Мой коллега указал, что я не использовал какую-то форму ReactRedux.useSelector()
в своих компонентах, поэтому он был озадачен тем, почему макеты работают; он прочитал из поста Уилла Окельманна-Вагнера Shallow Testing Hooks с ферментом , что это было требованием для useEffect
. Публикация гласит:
Единственное предостережение: если вы import React, { useEffect } from 'react'
в соответствии с рекомендациями документа, вы не собираетесь импортировать свою поддельную функцию, и ваш тест все равно будет неудачным , Но если вы просто используете React.useEffect
в своем компоненте, все будет работать нормально.
Я, будучи в замешательстве относительно того, почему возникли расхождения, клонировал репо Уилла и проверил его дальше. Это действительно не сработало, за исключением того, что я заметил, что у react-redux
нет экспорта по умолчанию, поэтому мне пришлось импортировать его как import * as ReactRedux from 'react-redux'
. Поэтому я попробовал то же самое в моем тестовом файле с React: import * as React from 'react'
и вот! Макеты работают отлично, и мне не нужно менять ни один из моих компонентов! Я полагаю, что благодаря счастливой случайности react-redux
заставил меня использовать метод импорта * as obj
.
Мой вопрос, однако, почему не они оба работают? Я попытался распечатать оба метода импорта объекта React
, и единственные различия были у этого метода импорта * as obj
было дополнительное свойство default
, которое было просто копией исходного объекта React
.
Более того, состояние документации Jest:
jest.spyOn (object, methodName)
Создает фиктивную функцию, аналогичную jest.fn
, но также отслеживает вызовы object[methodName]
.
Хорошо. Это означает, что пока метод useEffect
находится внутри объекта React
, он действительно будет издеваться над useEffect
. Ну, React
из import React ...
содержит содержит useEffect
, так почему же Jest не может шпионить за ним?
Ниже представлены две распечатки двух React
объектов в зависимости от метода импорта:
import React from 'react'
и import React, { useEffect } from 'react'
React: {
Children: {
map: [Function: mapChildren],
forEach: [Function: forEachChildren],
count: [Function: countChildren],
toArray: [Function: toArray],
only: [Function: onlyChild]
},
Component: [Function: Component],
Fragment: Symbol(react.fragment),
Profiler: Symbol(react.profiler),
PureComponent: [Function: PureComponent],
StrictMode: Symbol(react.strict_mode),
Suspense: Symbol(react.suspense),
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
ReactCurrentDispatcher: { current: null },
ReactCurrentBatchConfig: { suspense: null },
ReactCurrentOwner: { current: null },
IsSomeRendererActing: { current: false },
assign: [Function: assign],
ReactDebugCurrentFrame: { getCurrentStack: null, getStackAddendum: [Function] },
ReactComponentTreeHook: {}
},
cloneElement: [Function: cloneElementWithValidation],
createContext: [Function: createContext],
createElement: [Function: createElementWithValidation],
createFactory: [Function: createFactoryWithValidation],
createRef: [Function: createRef],
forwardRef: [Function: forwardRef],
isValidElement: [Function: isValidElement],
lazy: [Function: lazy],
memo: [Function: memo],
useCallback: [Function: useCallback],
useContext: [Function: useContext],
useDebugValue: [Function: useDebugValue],
useEffect: [Function: useEffect],
useImperativeHandle: [Function: useImperativeHandle],
useLayoutEffect: [Function: useLayoutEffect],
useMemo: [Function: useMemo],
useReducer: [Function: useReducer],
useRef: [Function: useRef],
useState: [Function: useState],
version: '16.13.1'
}
import * as React from 'react'
React: {
Children: {
map: [Function: mapChildren],
forEach: [Function: forEachChildren],
count: [Function: countChildren],
toArray: [Function: toArray],
only: [Function: onlyChild]
},
Component: [Function: Component],
Fragment: Symbol(react.fragment),
Profiler: Symbol(react.profiler),
PureComponent: [Function: PureComponent],
StrictMode: Symbol(react.strict_mode),
Suspense: Symbol(react.suspense),
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
ReactCurrentDispatcher: { current: null },
ReactCurrentBatchConfig: { suspense: null },
ReactCurrentOwner: { current: null },
IsSomeRendererActing: { current: false },
assign: [Function: assign],
ReactDebugCurrentFrame: { getCurrentStack: null, getStackAddendum: [Function] },
ReactComponentTreeHook: {}
},
cloneElement: [Function: cloneElementWithValidation],
createContext: [Function: createContext],
createElement: [Function: createElementWithValidation],
createFactory: [Function: createFactoryWithValidation],
createRef: [Function: createRef],
forwardRef: [Function: forwardRef],
isValidElement: [Function: isValidElement],
lazy: [Function: lazy],
memo: [Function: memo],
useCallback: [Function: useCallback],
useContext: [Function: useContext],
useDebugValue: [Function: useDebugValue],
useEffect: [Function: useEffect],
useImperativeHandle: [Function: useImperativeHandle],
useLayoutEffect: [Function: useLayoutEffect],
useMemo: [Function: useMemo],
useReducer: [Function: useReducer],
useRef: [Function: useRef],
useState: [Function: useState],
version: '16.13.1',
default: { // This is where the differences begin
Children: {
map: [Function: mapChildren],
forEach: [Function: forEachChildren],
count: [Function: countChildren],
toArray: [Function: toArray],
only: [Function: onlyChild]
},
Component: [Function: Component],
Fragment: Symbol(react.fragment),
Profiler: Symbol(react.profiler),
PureComponent: [Function: PureComponent],
StrictMode: Symbol(react.strict_mode),
Suspense: Symbol(react.suspense),
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
ReactCurrentDispatcher: [Object],
ReactCurrentBatchConfig: [Object],
ReactCurrentOwner: [Object],
IsSomeRendererActing: [Object],
assign: [Function: assign],
ReactDebugCurrentFrame: [Object],
ReactComponentTreeHook: {}
},
cloneElement: [Function: cloneElementWithValidation],
createContext: [Function: createContext],
createElement: [Function: createElementWithValidation],
createFactory: [Function: createFactoryWithValidation],
createRef: [Function: createRef],
forwardRef: [Function: forwardRef],
isValidElement: [Function: isValidElement],
lazy: [Function: lazy],
memo: [Function: memo],
useCallback: [Function: useCallback],
useContext: [Function: useContext],
useDebugValue: [Function: useDebugValue],
useEffect: [Function: useEffect],
useImperativeHandle: [Function: useImperativeHandle],
useLayoutEffect: [Function: useLayoutEffect],
useMemo: [Function: useMemo],
useReducer: [Function: useReducer],
useRef: [Function: useRef],
useState: [Function: useState],
version: '16.13.1'
}
}