reactjs 使用renderHook测试自定义钩子

ctrmrzij  于 2023-05-22  发布在  React
关注(0)|答案(2)|浏览(123)

我想测试一个自定义钩子,它是作为一个帮助函数实现的,用于与其他钩子的代码重用。它在其实现中调用useDispatchuseSelector,沿着将数据保存在会话存储中:

export function useCustomHook(key, obj)
  {
    let myObject = {
      somefield: obj.someField
    };
    sessionStorage.setItem(key, JSON.stringify(myObject));
    const dispatch = useDispatch();
    dispatch(actionCreator.addAction(key, myObject));
  }

测试:

it('should have data in redux and session storage', () =>
{
  const obj = { 
    somefield: 'my val', 
  };
  renderHook(() => useCustomHook('some key', obj));
  let savedObj= JSON.parse(sessionStorage.getItem('some key'));
  expect(savedObj.someField).toBe('my val');
  renderHook(() => 
  {
    console.log("INSIDE");
    let reduxObj = useSelector(state => state.vals);
    console.log("THE OBJECT: " );
    console.log(reduxObj);
    expect(reduxObj).toBe(2); //just to see if it fails the test - it's not
  });
})

无论我尝试什么,测试都只到达“INSIDE”控制台日志,而不会打印“THE OBJECT:“控制台日志。测试本身仍然通过,所以就像useSelector以某种方式停止了其余的renderHook执行。
我猜这和测试没有连接商店有关。。在这种情况下,可以做些什么来测试redux?

rhfm7lfc

rhfm7lfc1#

您需要提供一个wrapper组件,将redux Providerstore连接起来:

it('should have data in redux and session storage', () =>
{
  const obj = { 
    somefield: 'my val', 
  };

  const store = {} // create/mock a store
  const wrapper = ({ children }) => <Provider store={store}>{children}</Provider>

  renderHook(() => useCustomHook('some key', obj), { wrapper });
  let savedObj= JSON.parse(sessionStorage.getItem('some key'));
  expect(savedObj.someField).toBe('my val');
  renderHook(() => {
    console.log("INSIDE");
    let reduxObj = useSelector(state => state.vals);
    console.log("THE OBJECT: " );
    console.log(reduxObj);
    expect(reduxObj).toBe(2); //just to see if it fails the test - it's not
  }, { wrapper });
})

顺便说一句,renderHook正在捕获错误,这就是为什么你在测试中看不到它们,如果你试图访问result.current,它会抛出,你可以看到它在result.error中表示,但是这里的用法是不从自定义钩子返回一个值来Assert是非常不寻常的。
这也可能会因为在第二个renderHook调用中有Assert而导致问题。您可能希望从钩子返回值并在外部Assert,或者在redux存储中Assert更新后的值。

soat7uwm

soat7uwm2#

您可以编写一个简单的扩展redux renderWithProviders函数。这看起来像:

import React, { PropsWithChildren } from 'react'
import { render } from '@testing-library/react'
import type { RenderOptions } from '@testing-library/react'
import { configureStore } from '@reduxjs/toolkit'
import type { PreloadedState } from '@reduxjs/toolkit'
import { Provider } from 'react-redux';
import { renderHook } from '@testing-library/react';

export function renderHookWithProviders<
  Result,
  Props>(
    render: (initialProps: Props) => Result,
    {
      preloadedState = {},
      // Automatically create a store instance if no store was passed in
      store = configureStore({
        reducer: {
....
        },
        preloadedState
      }),
      ...renderOptions
    }: ExtendedRenderOptions = {}
  ) {
  function Wrapper({ children }: PropsWithChildren<{}>): JSX.Element {
    return <Provider store={store}>{children}</Provider>
  }

  return { store, ...renderHook(render, { wrapper: Wrapper, ...renderOptions }) }
}

请注意,render方法被替换为来自@testing-library/reactrenderHook。这可以在你的测试中简单地使用:

describe('useGetRelated', () => {
  test('Return related', () => {
    const { result } = renderHookWithProviders(() => useGetRelated(), {
      preloadedState: {
        //initial state of redux
      }
    });

  });
});

hooks testing library talk about this briefly as well虽然解决方案需要更多的锅炉板在每个测试然后redux主张一个。

相关问题