Jest.js react-navigation-hooks:如何测试useFocusEffect

yqlxgs2m  于 5个月前  发布在  Jest
关注(0)|答案(8)|浏览(86)

据我所知,应该这样做,以便useFocusEffect将作为useFocusEffect用于测试(模拟)。我使用useFocusEffect用于fetchData:

useFocusEffect(
  useCallback(() => {
    fetchData();
  }, [fetchData]),
);

字符串

**错误消息:**react-navigation hooks需要导航上下文,但找不到。请确保您没有忘记创建和渲染react-navigation应用容器。如果您需要访问可选的导航对象,可以使用Context(NavigationContext),它可能会返回
软件包版本:

"jest": "^24.9.0",
"react-native": "0.61.2",
"react-navigation": "^4.0.10",
"react-navigation-hooks": "^1.1.0",
"@testing-library/react-native": "^4.0.14",

tzxcd3kk

tzxcd3kk1#

假设你在测试中渲染你的组件,你需要将它 Package 在一个假的<NavigationContext>中。这样做可以让useFocusEffect查找它需要的东西,以确定该组件是否已被你的应用导航聚焦。
这个例子使用了react-native-testing-library中的render。我认为它类似于其他渲染方法。

import { NavigationContext } from "@react-navigation/native"
import { render } from "react-native-testing-library"

// fake NavigationContext value data
const navContext = {
  isFocused: () => true,
  // addListener returns an unscubscribe function.
  addListener: jest.fn(() => jest.fn())
}

// MyComponent needs to be inside an NavigationContext, to allow useFocusEffect to function.
const { toJSON } = render(
  <NavigationContext.Provider value={navContext}>
    <MyComponent />
  </NavigationContext.Provider>
)

字符串

8qgya5xd

8qgya5xd2#

这只是@meshantz上述回答的更完整版本。

import { NavigationContext } from '@react-navigation/native';
import { render } from '@testing-library/react-native';
import React from 'react';

// This would probably be imported from elsewhere...
const ComponentUnderTest = () => {
  useFocusEffect(
    useCallback(() => {
      fetchData();
    }, [fetchData]),
  );
  
  return null;
};

const mockFetchData = jest.fn();
jest.mock('fetchData', () => mockFetchData);

describe('testing useFocusOnEffect in ComponentUnderTest', () => {
  afterAll(() => {
    jest.restoreAllMocks();
  });

  describe('when the view comes into focus', () => {
    it('calls fetchData', () => {
      const navContextValue = {
        isFocused: () => false,
        addListener: jest.fn(() => jest.fn()),
      };

      render(
        <NavigationContext.Provider value={navContextValue}>
          <ComponentUnderTest />
        </NavigationContext.Provider>,
      );

      expect(mockFetchData).toBeCalledTimes(0);

      render(
        <NavigationContext.Provider
          value={{
            ...navContextValue,
            isFocused: () => true,
          }}
        >
          <ComponentUnderTest />
        </NavigationContext.Provider>,
      );

      expect(mockFetchData).toBeCalledTimes(1);
    });
  });
});

字符串

j5fpnvbx

j5fpnvbx3#

对于TypeScript,它也需要满足类型要求,所以在我的例子中,它是通过使用jest.requireActual完成的:

const withProvider = (element, store = defaultStore) => {
      // fake NavigationContext value data
      const actualNav = jest.requireActual("@react-navigation/native");
      const navContext = {
        ...actualNav.navigation,
        navigate: () => {},
        dangerouslyGetState: () => {},
        setOptions: () => {},
        addListener: () => () => {},
        isFocused: () => true,
      };
      return (
        <NavigationContext.Provider value={navContext}>
          <MyComponent />
        </NavigationContext.Provider>
      );
    };
    
    it("renders correctly", () => {
      render(withProvider(() => <SportsBooksScreen {...defaultProps} />));
    });

字符串

yduiuuwa

yduiuuwa4#

我在这个帖子中提出的解决方案有问题/限制,所以我最终用“React.useEffect”来模仿“useFocusEffect”。
它做得很好:我的测试现在是绿色的!

jest.mock('@react-navigation/native', () => {
  const { useEffect } = require('react');
  const actualModule = jest.requireActual('@react-navigation/native');

  return {
    ...actualModule,
    useFocusEffect: useEffect,
  };
});

字符串

14ifxucb

14ifxucb5#

而不是useFocusEffect,使用useIsFocused和useEffect,代码工作正常。

In Your component:

import React, { useEffect } from 'react';
import { useIsFocused } from '@react-navigation/native';

const Component = () => {
  const isFocused = useIsFocused();
useEffect(() => {
    if (isFocused) {
      fetchData();
    }
  }, [isFocused]);
  return (<><View testID="child_test_id">{'render child nodes'}</View></>)
}


For Testing:

import Component from '--path-to-component--';
jest.mock('--path-to-fetchData--');
jest.mock('@react-navigation/native', () => {
  return {
    useIsFocused: () => true
  };
});

it('should render child component when available', async () => {
  const mockedData = [];
  fetchData.mockImplementation(() => mockedData);
  let screen = null;
  await act(async () => {
    screen = renderer.create(<Component  />);
  });
  const childNode = screen.root.findByProps({ testID: 'child_test_id' });
  expect(childNode.children).toHaveLength(1);
});

字符串

gopyfrb3

gopyfrb36#

useFocusEffect使用navigation.isFocused(),可以从jest的renderHookWithProviders访问。
做一个navigation.isFocused.mockReturnValue(true);应该就行了!现在刚试过,效果很好。

yqyhoc1h

yqyhoc1h7#

创建组件FocusEffects

import { useFocusEffect } from "@react-navigation/native";
import { BackHandler } from "react-native";
import React from "react";

export default function FocusEffect({ onFocus, onFocusRemoved }) {
  useFocusEffect(
    React.useCallback(() => {
      onFocus();

      return () => onFocusRemoved();
    }, [onFocus, onFocusRemoved]),
  );
  return null;
}

字符串
使用示例:

import React from 'react';
import { Text, View } from 'react-native';
import { FocusEffect } from './components';
    
const App = () => {

onFocus = () => {
   // ============>>>> onFocus <<<<==============
   fetchData();
};

onFocusRemoved = () => {
   // ============>>>> onFocusRemoved <<<<==============
};

return (
    <View>
       <FocusEffect
            onFocus={this.onFocus}
            onFocusRemoved={this.onFocusRemoved}
       />
       <Text>Hello, world!</Text>
   </View>
   )
}
export default App;

yfwxisqw

yfwxisqw8#

如果useFocusEffect()中的代码对您的测试没有影响,您可以模拟钩子,如下所示:

jest.mock("@react-navigation/native", () => ({
  useFocusEffect: jest.fn(),
  // ...
}));

字符串

相关问题