测试自定义钩子:不变违规:找不到 react-redux 上下文值;请确保组件包装在 <Provider>

Posted

技术标签:

【中文标题】测试自定义钩子:不变违规:找不到 react-redux 上下文值;请确保组件包装在 <Provider>【英文标题】:Testing custom hook: Invariant Violation: could not find react-redux context value; please ensure the component is wrapped in a <Provider> 【发布时间】:2020-04-16 00:24:19 【问题描述】:

我有一个想要测试的自定义钩子。它接收一个 redux store 调度函数并返回一个函数。为了得到我想要做的结果:

const  result  = renderHook(() =>  useSaveAuthenticationDataToStorages(useDispatch()));

但是,我收到一个错误:

Invariant Violation:找不到 react-redux 上下文值;请确保组件被包裹在一个

这是因为useDispatch 并且没有连接商店。但是,我这里没有任何组件可以与提供程序一起包装。我只需要测试挂钩,它只是将数据保存到商店。

我该如何解决?

【问题讨论】:

【参考方案1】:

react hooks 测试库docs 对此进行了更深入的研究。但是,我们本质上缺少的是可以通过创建包装器获得的提供程序。首先我们声明一个组件作为我们的提供者:

import  Provider  from 'react-redux'

const ReduxProvider = ( children, reduxStore ) => (
  <Provider store=reduxStore>children</Provider>
)

那么在我们的测试中我们调用

test("...", () => 
  const store = configureStore();
  const wrapper = ( children ) => (
    <ReduxProvider reduxStore=store>children</ReduxProvider>
  );
  const  result  = renderHook(() => 
    useSaveAuthenticationDataToStorages(useDispatch());
  ,  wrapper );
  // ... Rest of the logic
);

【讨论】:

请注意,如果你把这个 ReduxProvider 放在它自己的文件中(就像我一样),你需要导入 react 才能让它工作 import React from 'react'; configureStore() 是什么?【参考方案2】:

这可能是一个较晚的答案,但您也可以在测试中使用它

jest.mock('react-redux', () => 
    const ActualReactRedux = jest.requireActual('react-redux');
    return 
        ...ActualReactRedux,
        useSelector: jest.fn().mockImplementation(() => 
            return mockState;
        ),
    ;
);

【讨论】:

我不确定我是否理解这一点。我将此添加到测试中,但我仍然看到有关缺少 &lt;Provider&gt; 的错误。你能扩展这个答案吗? 考虑提供完整的答案。 我收到一个错误TypeError: require.requireActual is not a function,你知道为什么吗? @Sachihiro 你需要jest.requireActual 而不是require.requireActual 似乎可行,但您将无法在测试用例中更新 mockState?【参考方案3】:

此问题与您的测试文件有关。您必须在测试文件中声明 providerstore

通过以下代码更新或替换您的app.test.tsx

注意:如果您还没有安装redux-mock-store,请不要忘记安装。

import React from 'react';
import  render  from '@testing-library/react';

import App from './App';

import  Provider  from 'react-redux';
import configureStore from 'redux-mock-store';

describe('With React Testing Library', () => 
    const initialState =  output: 10 ;
    const mockStore = configureStore();
    let store;

    it('Shows "Hello world!"', () => 
        store = mockStore(initialState);
        const  getByText  = render(
            <Provider store=store>
                <App />
            </Provider>
        );

        expect(getByText('Hello World!')).not.toBeNull();
    );
);

我在搜索 1 小时后得到了这个解决方案。 非常感谢OSTE

原解决方案:Github issues/8145 and solution link


使用此解决方案,如果您遇到 TypeError: window.matchMedia is not a function 之类的错误,请通过此方式解决。将这些行添加到您的 setupTests.ts 文件中。原解决方案链接***.com/a/64872224/5404861

global.matchMedia = global.matchMedia || function () 
  return 
    addListener: jest.fn(),
    removeListener: jest.fn(),
  ;
;

【讨论】:

【参考方案4】:

我认为您可以像这样创建test-utils.[j|t]s(?x)whatever you set the name of the file to

https://github.com/hafidzamr/nextjs-ts-redux-toolkit-quickstart/blob/main/__tests__/test-utils.tsx

//root(or wherever your the file)/test-utils.tsx

import React from 'react';
import  render, RenderOptions  from '@testing-library/react';
import  Provider  from 'react-redux';

// Import your store
import  store  from '@/store';

const Wrapper: React.FC = ( children ) => <Provider store=store>children</Provider>;

const customRender = (ui: React.ReactElement, options?: Omit<RenderOptions, 'wrapper'>) => render(ui,  wrapper: Wrapper, ...options );

// re-export everything
export * from '@testing-library/react';
// override render method
export  customRender as render ;


像这样使用它: https://github.com/hafidzamr/nextjs-ts-redux-toolkit-quickstart/blob/main/__tests__/pages/index.test.tsx

//__tests__/pages/index.test.tsx


import React from 'react';
import  render, screen  from '../test-utils';
import Home from '@/pages/index';

describe('Home Pages', () => 
  test('Should be render', () => 
    render(<Home />);
    const getAText = screen.getByTestId('welcome');
    expect(getAText).toBeInTheDocument();
  );
);


为我工作。

screenshot work

顺便说一句,如果您将test-utils.[j|t]s(?x)whatever you set the name file 放在目录__test__ 上,不要忘记在jest.config.js 上忽略它。

//jest.config.js

testPathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/.next/', '<rootDir>/__tests__/test-utils.tsx'],

回购:https://github.com/hafidzamr/nextjs-ts-redux-toolkit-quickstart

【讨论】:

以上是关于测试自定义钩子:不变违规:找不到 react-redux 上下文值;请确保组件包装在 <Provider>的主要内容,如果未能解决你的问题,请参考以下文章

不变违规:TurboModuleRegistry.getEnforcing(...): 'NativeReanimated' 找不到

不变违规:requireNativeComponent:在 UIManager 中找不到“AIRMap”。没有修复?

不变违规:requireNativeComponent:在反应原生的 UIManager 中找不到“FastImageView”

JestJS -> 不变违规:在“连接(投资组合)”的上下文或道具中找不到“商店”

next.js + GraphQL:“不变违规:在上下文中找不到“客户端”或作为选项传入......”

不变违规:requireNativeComponent:在 UIManager 中找不到“RNCSafeAreaView”。在 expo 应用程序中反应原生