从单元测试中的导航模拟 addListener

Mock addListener from navigation in unit test

像这样从导航中使用 addListener,

useEffect(() => {
 const navigationSubscription = props.navigation.addListener(
   "willFocus",
   () => setFocused(true)
 );
 return navigationSubscription.remove; //navigationSubscription is undefined here.
}, []);

这是测试文件中的代码片段,

const componentStub = (props) => {
  return (
    <Provider store={store}>
      <TestComponent            
        navigation={{
          navigate: jest.fn(),
          addListener: jest.fn().mockImplementation((event, callback) => {
            callback();
            //remove: jest.fn();
          }),
          canGoBack: jest.fn(),
          dispatch: jest.fn(),
          getParent: jest.fn(),
          getState: jest.fn(),
          goBack: jest.fn(),
          isFocused: jest.fn(),
          pop: jest.fn(),
          popToTop: jest.fn(),
          push: jest.fn(),
          removeListener: jest.fn(),
          replace: jest.fn(),
          reset: jest.fn(),
          setOptions: jest.fn(),
          setParams: jest.fn(),
          // remove: jest.fn(),
        }}
        {...props}
      />
    </Provider>
  );
};
describe("TestComponent unit tests", () => {
 it("Should render correctly", () => {
   let componentUtils = render(componentStub());
   const { toJSON } = componentUtils;
   expect(toJSON().children.length).toBeGreaterThan(0);
 });
});

我得到 TypeError: Cannot read properties of undefined (reading remove)

也许导航订阅尚未分配导致该错误,为了避免,我想在调用之前设置一个条件:

useEffect(() => {
 const navigationSubscription = props.navigation.addListener(
   "willFocus",
   () => setFocused(true)
 );
 if(navigationSubscription){
    return navigationSubscription.remove; //<=== here. To surely: return navigationSubscription?.remove
  }
 
}, [props.navigation]); //<=== Then pass props.navigation to make component re-render when it change

mocked addListener 有一个小问题。当您调用 addListener 时,它期待返回值,但您没有从该模拟函数返回任何内容。

可能的解决方法是

const componentStub = (props) => {
  return (
    <Provider store={store}>
      <TestComponent            
        navigation={{
          navigate: jest.fn(),
          addListener: jest.fn().mockImplementation((event, callback) => {
            callback();
            //returning value for `navigationSubscription`
            return {
               remove: jest.fn()
            }
          }),
          canGoBack: jest.fn(),
          dispatch: jest.fn(),
          getParent: jest.fn(),
          getState: jest.fn(),
          goBack: jest.fn(),
          isFocused: jest.fn(),
          pop: jest.fn(),
          popToTop: jest.fn(),
          push: jest.fn(),
          removeListener: jest.fn(),
          replace: jest.fn(),
          reset: jest.fn(),
          setOptions: jest.fn(),
          setParams: jest.fn(),
          // remove: jest.fn(),
        }}
        {...props}
      />
    </Provider>
  );
};
describe("TestComponent unit tests", () => {
 it("Should render correctly", () => {
   let componentUtils = render(componentStub());
   const { toJSON } = componentUtils;
   expect(toJSON().children.length).toBeGreaterThan(0);
 });
});